summaryrefslogtreecommitdiffstats
path: root/server/shadow
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--server/shadow/CMakeLists.txt245
-rw-r--r--server/shadow/FreeRDP-ShadowConfig.cmake.in14
-rw-r--r--server/shadow/Mac/CMakeLists.txt31
-rw-r--r--server/shadow/Mac/mac_shadow.c680
-rw-r--r--server/shadow/Mac/mac_shadow.h64
-rw-r--r--server/shadow/Win/CMakeLists.txt20
-rw-r--r--server/shadow/Win/win_dxgi.c794
-rw-r--r--server/shadow/Win/win_dxgi.h61
-rw-r--r--server/shadow/Win/win_rdp.c440
-rw-r--r--server/shadow/Win/win_rdp.h56
-rw-r--r--server/shadow/Win/win_shadow.c560
-rw-r--r--server/shadow/Win/win_shadow.h89
-rw-r--r--server/shadow/Win/win_wds.c850
-rw-r--r--server/shadow/Win/win_wds.h48
-rw-r--r--server/shadow/X11/CMakeLists.txt72
-rw-r--r--server/shadow/X11/x11_shadow.c1513
-rw-r--r--server/shadow/X11/x11_shadow.h114
-rw-r--r--server/shadow/freerdp-shadow-cli.1.in85
-rw-r--r--server/shadow/freerdp-shadow.pc.in15
-rw-r--r--server/shadow/shadow.c179
-rw-r--r--server/shadow/shadow.h44
-rw-r--r--server/shadow/shadow_audin.c104
-rw-r--r--server/shadow/shadow_audin.h39
-rw-r--r--server/shadow/shadow_capture.c263
-rw-r--r--server/shadow/shadow_capture.h52
-rw-r--r--server/shadow/shadow_channels.c66
-rw-r--r--server/shadow/shadow_channels.h45
-rw-r--r--server/shadow/shadow_client.c2612
-rw-r--r--server/shadow/shadow_client.h35
-rw-r--r--server/shadow/shadow_encoder.c520
-rw-r--r--server/shadow/shadow_encoder.h81
-rw-r--r--server/shadow/shadow_encomsp.c129
-rw-r--r--server/shadow/shadow_encomsp.h39
-rw-r--r--server/shadow/shadow_input.c114
-rw-r--r--server/shadow/shadow_input.h35
-rw-r--r--server/shadow/shadow_lobby.c85
-rw-r--r--server/shadow/shadow_lobby.h40
-rw-r--r--server/shadow/shadow_mcevent.c355
-rw-r--r--server/shadow/shadow_mcevent.h56
-rw-r--r--server/shadow/shadow_rdpgfx.c55
-rw-r--r--server/shadow/shadow_rdpgfx.h39
-rw-r--r--server/shadow/shadow_rdpsnd.c87
-rw-r--r--server/shadow/shadow_rdpsnd.h39
-rw-r--r--server/shadow/shadow_remdesk.c50
-rw-r--r--server/shadow/shadow_remdesk.h39
-rw-r--r--server/shadow/shadow_screen.c163
-rw-r--r--server/shadow/shadow_screen.h55
-rw-r--r--server/shadow/shadow_server.c1028
-rw-r--r--server/shadow/shadow_subsystem.c286
-rw-r--r--server/shadow/shadow_subsystem.h47
-rw-r--r--server/shadow/shadow_subsystem_builtin.c75
-rw-r--r--server/shadow/shadow_surface.c104
-rw-r--r--server/shadow/shadow_surface.h45
53 files changed, 12756 insertions, 0 deletions
diff --git a/server/shadow/CMakeLists.txt b/server/shadow/CMakeLists.txt
new file mode 100644
index 0000000..72adcca
--- /dev/null
+++ b/server/shadow/CMakeLists.txt
@@ -0,0 +1,245 @@
+# FreeRDP: A Remote Desktop Protocol Implementation
+# FreeRDP Shadow Server cmake build script
+#
+# Copyright 2014 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.
+
+# freerdp-shadow library
+
+set(MODULE_NAME "freerdp-shadow")
+
+set(SRCS
+ shadow_client.c
+ shadow_client.h
+ shadow_lobby.c
+ shadow_lobby.h
+ shadow_input.c
+ shadow_input.h
+ shadow_screen.c
+ shadow_screen.h
+ shadow_surface.c
+ shadow_surface.h
+ shadow_encoder.c
+ shadow_encoder.h
+ shadow_capture.c
+ shadow_capture.h
+ shadow_channels.c
+ shadow_channels.h
+ shadow_encomsp.c
+ shadow_encomsp.h
+ shadow_remdesk.c
+ shadow_remdesk.h
+ shadow_rdpsnd.c
+ shadow_rdpsnd.h
+ shadow_audin.c
+ shadow_audin.h
+ shadow_rdpgfx.c
+ shadow_rdpgfx.h
+ shadow_subsystem.c
+ shadow_subsystem.h
+ shadow_mcevent.c
+ shadow_mcevent.h
+ shadow_server.c
+ shadow.h)
+
+if (NOT FREERDP_UNIFIED_BUILD)
+ find_package(rdtk 0 REQUIRED)
+ include_directories(${RDTK_INCLUDE_DIR})
+else()
+ if (NOT WITH_RDTK)
+ message(FATAL_ERROR "-DWITH_RDTK=ON is required for unified FreeRDP build with shadow server")
+ endif()
+ include_directories(${PROJECT_SOURCE_DIR}/rdtk/include)
+ include_directories(${PROJECT_BINARY_DIR}/rdtk/include)
+endif()
+
+# On windows create dll version information.
+# Vendor, product and year are already set in top level CMakeLists.txt
+if (WIN32)
+ set (RC_VERSION_MAJOR ${FREERDP_VERSION_MAJOR})
+ set (RC_VERSION_MINOR ${FREERDP_VERSION_MINOR})
+ set (RC_VERSION_BUILD ${FREERDP_VERSION_REVISION})
+ set (RC_VERSION_PATCH 0)
+ set (RC_VERSION_FILE "${CMAKE_SHARED_LIBRARY_PREFIX}${MODULE_NAME}${FREERDP_API_VERSION}${CMAKE_SHARED_LIBRARY_SUFFIX}" )
+
+ configure_file(
+ ${PROJECT_SOURCE_DIR}/cmake/WindowsDLLVersion.rc.in
+ ${CMAKE_CURRENT_BINARY_DIR}/version.rc
+ @ONLY)
+
+list (APPEND SRCS ${CMAKE_CURRENT_BINARY_DIR}/version.rc)
+endif()
+
+add_library(${MODULE_NAME} ${SRCS})
+
+list(APPEND LIBS
+ freerdp
+ freerdp-server
+ winpr
+ winpr-tools
+ rdtk
+)
+
+target_include_directories(${MODULE_NAME} INTERFACE $<INSTALL_INTERFACE:include>)
+target_link_libraries(${MODULE_NAME} PRIVATE ${LIBS})
+
+set_target_properties(${MODULE_NAME} PROPERTIES OUTPUT_NAME ${MODULE_NAME}${FREERDP_VERSION_MAJOR})
+if (WITH_LIBRARY_VERSIONING)
+ set_target_properties(${MODULE_NAME} PROPERTIES VERSION ${FREERDP_VERSION} SOVERSION ${FREERDP_API_VERSION})
+endif()
+
+install(TARGETS ${MODULE_NAME} COMPONENT server EXPORT FreeRDP-ShadowTargets
+ ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
+
+if (WITH_DEBUG_SYMBOLS AND MSVC)
+ get_target_property(OUTPUT_FILENAME ${MODULE_NAME} OUTPUT_NAME)
+ install(FILES ${PROJECT_BINARY_DIR}/${OUTPUT_FILENAME}.pdb DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT symbols)
+endif()
+
+set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Server/shadow")
+
+# subsystem library
+
+set(MODULE_NAME "freerdp-shadow-subsystem")
+
+set(SRCS
+ shadow_subsystem_builtin.c)
+
+if(WIN32)
+ add_subdirectory(Win)
+elseif(NOT APPLE)
+ add_subdirectory(X11)
+elseif(APPLE AND NOT IOS)
+ add_subdirectory(Mac)
+endif()
+
+if (WIN32)
+ set (RC_VERSION_MAJOR ${FREERDP_VERSION_MAJOR})
+ set (RC_VERSION_MINOR ${FREERDP_VERSION_MINOR})
+ set (RC_VERSION_BUILD ${FREERDP_VERSION_REVISION})
+ set (RC_VERSION_FILE "${MODULE_NAME}${CMAKE_EXECUTABLE_SUFFIX}" )
+
+ configure_file(
+ ${PROJECT_SOURCE_DIR}/cmake/WindowsDLLVersion.rc.in
+ ${CMAKE_CURRENT_BINARY_DIR}/version.rc
+ @ONLY)
+
+ set ( SRCS ${SRCS} ${CMAKE_CURRENT_BINARY_DIR}/version.rc)
+endif()
+
+add_library(${MODULE_NAME} ${SRCS})
+
+list(APPEND LIBS
+ freerdp-shadow-subsystem-impl
+ freerdp-shadow
+ freerdp
+ winpr
+)
+
+target_include_directories(${MODULE_NAME} INTERFACE $<INSTALL_INTERFACE:include>)
+target_link_libraries(${MODULE_NAME} PRIVATE ${LIBS})
+
+set_target_properties(${MODULE_NAME} PROPERTIES OUTPUT_NAME ${MODULE_NAME}${FREERDP_API_VERSION})
+if (WITH_LIBRARY_VERSIONING)
+ set_target_properties(${MODULE_NAME} PROPERTIES VERSION ${FREERDP_VERSION} SOVERSION ${FREERDP_API_VERSION})
+endif()
+
+if (NOT BUILD_SHARED_LIBS)
+ install(TARGETS freerdp-shadow-subsystem-impl
+ DESTINATION ${CMAKE_INSTALL_LIBDIR}
+ EXPORT FreeRDP-ShadowTargets
+ )
+endif()
+
+install(TARGETS ${MODULE_NAME} COMPONENT server EXPORT FreeRDP-ShadowTargets
+ ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
+
+if (WITH_DEBUG_SYMBOLS AND MSVC)
+ get_target_property(OUTPUT_FILENAME ${MODULE_NAME} OUTPUT_NAME)
+ install(FILES ${PROJECT_BINARY_DIR}/${OUTPUT_FILENAME}.pdb DESTINATION ${CMAKE_INSTALL_LIBDIR}
+ COMPONENT symbols)
+endif()
+
+set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Server/shadow")
+
+# command-line executable
+
+set(MODULE_NAME "freerdp-shadow-cli")
+
+set(SRCS
+ shadow.c)
+
+# On windows create dll version information.
+# Vendor, product and year are already set in top level CMakeLists.txt
+if (WIN32)
+ set (RC_VERSION_MAJOR ${FREERDP_VERSION_MAJOR})
+ set (RC_VERSION_MINOR ${FREERDP_VERSION_MINOR})
+ set (RC_VERSION_BUILD ${FREERDP_VERSION_REVISION})
+ set (RC_VERSION_FILE "${MODULE_NAME}${CMAKE_EXECUTABLE_SUFFIX}" )
+
+ configure_file(
+ ${PROJECT_SOURCE_DIR}/cmake/WindowsDLLVersion.rc.in
+ ${CMAKE_CURRENT_BINARY_DIR}/version.rc
+ @ONLY)
+
+ set ( SRCS ${SRCS} ${CMAKE_CURRENT_BINARY_DIR}/version.rc)
+endif()
+
+add_executable(${MODULE_NAME} ${SRCS})
+
+set(MANPAGE_NAME "${MODULE_NAME}")
+if (WITH_BINARY_VERSIONING)
+ set_target_properties(${MODULE_NAME} PROPERTIES OUTPUT_NAME "${MODULE_NAME}${FREERDP_API_VERSION}")
+ set(MANPAGE_NAME "${MODULE_NAME}${FREERDP_API_VERSION}")
+endif()
+
+list(APPEND LIBS freerdp-shadow-subsystem freerdp-shadow freerdp winpr)
+
+target_link_libraries(${MODULE_NAME} PRIVATE ${LIBS})
+
+install(TARGETS ${MODULE_NAME} DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT server)
+if (WITH_DEBUG_SYMBOLS AND MSVC)
+ install(FILES ${PROJECT_BINARY_DIR}/${MODULE_NAME}.pdb DESTINATION ${CMAKE_INSTALL_BINDIR}
+ COMPONENT symbols)
+endif()
+
+set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Server/shadow")
+
+include(pkg-config-install-prefix)
+configure_file(${CMAKE_CURRENT_SOURCE_DIR}/freerdp-shadow.pc.in ${CMAKE_CURRENT_BINARY_DIR}/freerdp-shadow${FREERDP_VERSION_MAJOR}.pc @ONLY)
+configure_file(freerdp-shadow-cli.1.in ${CMAKE_CURRENT_BINARY_DIR}/${MANPAGE_NAME}.1)
+install_freerdp_man(${CMAKE_CURRENT_BINARY_DIR}/${MANPAGE_NAME}.1 1)
+
+install(FILES ${CMAKE_CURRENT_BINARY_DIR}/freerdp-shadow${FREERDP_VERSION_MAJOR}.pc DESTINATION ${PKG_CONFIG_PC_INSTALL_DIR})
+
+export(PACKAGE freerdp-shadow)
+
+SetFreeRDPCMakeInstallDir(FREERDP_SERVER_CMAKE_INSTALL_DIR "FreeRDP-Shadow${FREERDP_VERSION_MAJOR}")
+
+configure_package_config_file(FreeRDP-ShadowConfig.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/FreeRDP-ShadowConfig.cmake
+ INSTALL_DESTINATION ${FREERDP_SERVER_CMAKE_INSTALL_DIR}
+ PATH_VARS FREERDP_INCLUDE_DIR)
+
+write_basic_package_version_file(${CMAKE_CURRENT_BINARY_DIR}/FreeRDP-ShadowConfigVersion.cmake
+ VERSION ${FREERDP_VERSION} COMPATIBILITY SameMajorVersion)
+
+install(FILES ${CMAKE_CURRENT_BINARY_DIR}/FreeRDP-ShadowConfig.cmake
+ ${CMAKE_CURRENT_BINARY_DIR}/FreeRDP-ShadowConfigVersion.cmake
+ DESTINATION ${FREERDP_SERVER_CMAKE_INSTALL_DIR})
+
+install(EXPORT FreeRDP-ShadowTargets DESTINATION ${FREERDP_SERVER_CMAKE_INSTALL_DIR})
diff --git a/server/shadow/FreeRDP-ShadowConfig.cmake.in b/server/shadow/FreeRDP-ShadowConfig.cmake.in
new file mode 100644
index 0000000..9b6f24c
--- /dev/null
+++ b/server/shadow/FreeRDP-ShadowConfig.cmake.in
@@ -0,0 +1,14 @@
+include(CMakeFindDependencyMacro)
+find_dependency(WinPR @FREERDP_VERSION@)
+find_dependency(FreeRDP @FREERDP_VERSION@)
+find_dependency(FreeRDP-Server @FREERDP_VERSION@)
+
+@PACKAGE_INIT@
+
+set(FreeRDP-Shadow_VERSION_MAJOR "@FREERDP_VERSION_MAJOR@")
+set(FreeRDP-Shadow_VERSION_MINOR "@FREERDP_VERSION_MINOR@")
+set(FreeRDP-Shadow_VERSION_REVISION "@FREERDP_VERSION_REVISION@")
+
+set_and_check(FreeRDP-Shadow_INCLUDE_DIR "@PACKAGE_FREERDP_INCLUDE_DIR@")
+
+include("${CMAKE_CURRENT_LIST_DIR}/FreeRDP-ShadowTargets.cmake")
diff --git a/server/shadow/Mac/CMakeLists.txt b/server/shadow/Mac/CMakeLists.txt
new file mode 100644
index 0000000..20c9a7b
--- /dev/null
+++ b/server/shadow/Mac/CMakeLists.txt
@@ -0,0 +1,31 @@
+
+include (WarnUnmaintained)
+warn_unmaintained("mac shadow server subsystem")
+
+find_library(IOKIT IOKit REQUIRED)
+find_library(IOSURFACE IOSurface REQUIRED)
+find_library(CARBON Carbon REQUIRED)
+find_package(PAM)
+
+set(LIBS
+ ${IOKIT}
+ ${IOSURFACE}
+ ${CARBON}
+)
+
+if(PAM_FOUND)
+ add_definitions(-DWITH_PAM)
+ include_directories(${PAM_INCLUDE_DIR})
+ list(APPEND LIBS ${PAM_LIBRARY})
+else()
+ message("building without PAM authentication support")
+endif()
+
+add_definitions(-DWITH_SHADOW_MAC)
+add_library(freerdp-shadow-subsystem-impl STATIC
+ mac_shadow.h
+ mac_shadow.c
+)
+target_link_libraries(freerdp-shadow-subsystem-impl PRIVATE
+ ${LIBS}
+)
diff --git a/server/shadow/Mac/mac_shadow.c b/server/shadow/Mac/mac_shadow.c
new file mode 100644
index 0000000..ba6d2b0
--- /dev/null
+++ b/server/shadow/Mac/mac_shadow.c
@@ -0,0 +1,680 @@
+/**
+ * FreeRDP: A Remote Desktop Protocol Implementation
+ *
+ * Copyright 2011-2014 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.
+ */
+
+#include <winpr/crt.h>
+#include <winpr/synch.h>
+#include <winpr/input.h>
+#include <winpr/sysinfo.h>
+
+#include <freerdp/server/server-common.h>
+#include <freerdp/codec/color.h>
+#include <freerdp/codec/region.h>
+#include <freerdp/log.h>
+
+#include "mac_shadow.h"
+
+#define TAG SERVER_TAG("shadow.mac")
+
+static macShadowSubsystem* g_Subsystem = NULL;
+
+static BOOL mac_shadow_input_synchronize_event(rdpShadowSubsystem* subsystem,
+ rdpShadowClient* client, UINT32 flags)
+{
+ if (!subsystem || !client)
+ return FALSE;
+
+ return TRUE;
+}
+
+static BOOL mac_shadow_input_keyboard_event(rdpShadowSubsystem* subsystem, rdpShadowClient* client,
+ UINT16 flags, UINT8 code)
+{
+ DWORD vkcode;
+ DWORD keycode;
+ BOOL extended;
+ CGEventRef kbdEvent;
+ CGEventSourceRef source;
+ extended = (flags & KBD_FLAGS_EXTENDED) ? TRUE : FALSE;
+
+ if (!subsystem || !client)
+ return FALSE;
+
+ if (extended)
+ code |= KBDEXT;
+
+ vkcode = GetVirtualKeyCodeFromVirtualScanCode(code, 4);
+
+ if (extended)
+ vkcode |= KBDEXT;
+
+ keycode = GetKeycodeFromVirtualKeyCode(vkcode, WINPR_KEYCODE_TYPE_APPLE);
+
+ source = CGEventSourceCreate(kCGEventSourceStateHIDSystemState);
+
+ if (flags & KBD_FLAGS_DOWN)
+ {
+ kbdEvent = CGEventCreateKeyboardEvent(source, (CGKeyCode)keycode, TRUE);
+ CGEventPost(kCGHIDEventTap, kbdEvent);
+ CFRelease(kbdEvent);
+ }
+ else if (flags & KBD_FLAGS_RELEASE)
+ {
+ kbdEvent = CGEventCreateKeyboardEvent(source, (CGKeyCode)keycode, FALSE);
+ CGEventPost(kCGHIDEventTap, kbdEvent);
+ CFRelease(kbdEvent);
+ }
+
+ CFRelease(source);
+ return TRUE;
+}
+
+static BOOL mac_shadow_input_unicode_keyboard_event(rdpShadowSubsystem* subsystem,
+ rdpShadowClient* client, UINT16 flags,
+ UINT16 code)
+{
+ if (!subsystem || !client)
+ return FALSE;
+
+ return TRUE;
+}
+
+static BOOL mac_shadow_input_mouse_event(rdpShadowSubsystem* subsystem, rdpShadowClient* client,
+ UINT16 flags, UINT16 x, UINT16 y)
+{
+ macShadowSubsystem* mac = (macShadowSubsystem*)subsystem;
+ UINT32 scrollX = 0;
+ UINT32 scrollY = 0;
+ CGWheelCount wheelCount = 2;
+
+ if (!subsystem || !client)
+ return FALSE;
+
+ if (flags & PTR_FLAGS_WHEEL)
+ {
+ scrollY = flags & WheelRotationMask;
+
+ if (flags & PTR_FLAGS_WHEEL_NEGATIVE)
+ {
+ scrollY = -(flags & WheelRotationMask) / 392;
+ }
+ else
+ {
+ scrollY = (flags & WheelRotationMask) / 120;
+ }
+
+ CGEventSourceRef source = CGEventSourceCreate(kCGEventSourceStateHIDSystemState);
+ CGEventRef scroll = CGEventCreateScrollWheelEvent(source, kCGScrollEventUnitLine,
+ wheelCount, scrollY, scrollX);
+ CGEventPost(kCGHIDEventTap, scroll);
+ CFRelease(scroll);
+ CFRelease(source);
+ }
+ else
+ {
+ CGEventSourceRef source = CGEventSourceCreate(kCGEventSourceStateHIDSystemState);
+ CGEventType mouseType = kCGEventNull;
+ CGMouseButton mouseButton = kCGMouseButtonLeft;
+
+ if (flags & PTR_FLAGS_MOVE)
+ {
+ if (mac->mouseDownLeft)
+ mouseType = kCGEventLeftMouseDragged;
+ else if (mac->mouseDownRight)
+ mouseType = kCGEventRightMouseDragged;
+ else if (mac->mouseDownOther)
+ mouseType = kCGEventOtherMouseDragged;
+ else
+ mouseType = kCGEventMouseMoved;
+
+ CGEventRef move =
+ CGEventCreateMouseEvent(source, mouseType, CGPointMake(x, y), mouseButton);
+ CGEventPost(kCGHIDEventTap, move);
+ CFRelease(move);
+ }
+
+ if (flags & PTR_FLAGS_BUTTON1)
+ {
+ mouseButton = kCGMouseButtonLeft;
+
+ if (flags & PTR_FLAGS_DOWN)
+ {
+ mouseType = kCGEventLeftMouseDown;
+ mac->mouseDownLeft = TRUE;
+ }
+ else
+ {
+ mouseType = kCGEventLeftMouseUp;
+ mac->mouseDownLeft = FALSE;
+ }
+ }
+ else if (flags & PTR_FLAGS_BUTTON2)
+ {
+ mouseButton = kCGMouseButtonRight;
+
+ if (flags & PTR_FLAGS_DOWN)
+ {
+ mouseType = kCGEventRightMouseDown;
+ mac->mouseDownRight = TRUE;
+ }
+ else
+ {
+ mouseType = kCGEventRightMouseUp;
+ mac->mouseDownRight = FALSE;
+ }
+ }
+ else if (flags & PTR_FLAGS_BUTTON3)
+ {
+ mouseButton = kCGMouseButtonCenter;
+
+ if (flags & PTR_FLAGS_DOWN)
+ {
+ mouseType = kCGEventOtherMouseDown;
+ mac->mouseDownOther = TRUE;
+ }
+ else
+ {
+ mouseType = kCGEventOtherMouseUp;
+ mac->mouseDownOther = FALSE;
+ }
+ }
+
+ CGEventRef mouseEvent =
+ CGEventCreateMouseEvent(source, mouseType, CGPointMake(x, y), mouseButton);
+ CGEventPost(kCGHIDEventTap, mouseEvent);
+ CFRelease(mouseEvent);
+ CFRelease(source);
+ }
+
+ return TRUE;
+}
+
+static BOOL mac_shadow_input_extended_mouse_event(rdpShadowSubsystem* subsystem,
+ rdpShadowClient* client, UINT16 flags, UINT16 x,
+ UINT16 y)
+{
+ if (!subsystem || !client)
+ return FALSE;
+
+ return TRUE;
+}
+
+static int mac_shadow_detect_monitors(macShadowSubsystem* subsystem)
+{
+ size_t wide, high;
+ MONITOR_DEF* monitor;
+ CGDirectDisplayID displayId;
+ displayId = CGMainDisplayID();
+ CGDisplayModeRef mode = CGDisplayCopyDisplayMode(displayId);
+ subsystem->pixelWidth = CGDisplayModeGetPixelWidth(mode);
+ subsystem->pixelHeight = CGDisplayModeGetPixelHeight(mode);
+ wide = CGDisplayPixelsWide(displayId);
+ high = CGDisplayPixelsHigh(displayId);
+ CGDisplayModeRelease(mode);
+ subsystem->retina = ((subsystem->pixelWidth / wide) == 2) ? TRUE : FALSE;
+
+ if (subsystem->retina)
+ {
+ subsystem->width = wide;
+ subsystem->height = high;
+ }
+ else
+ {
+ subsystem->width = subsystem->pixelWidth;
+ subsystem->height = subsystem->pixelHeight;
+ }
+
+ subsystem->common.numMonitors = 1;
+ monitor = &(subsystem->common.monitors[0]);
+ monitor->left = 0;
+ monitor->top = 0;
+ monitor->right = subsystem->width;
+ monitor->bottom = subsystem->height;
+ monitor->flags = 1;
+ return 1;
+}
+
+static int mac_shadow_capture_start(macShadowSubsystem* subsystem)
+{
+ CGError err;
+ err = CGDisplayStreamStart(subsystem->stream);
+
+ if (err != kCGErrorSuccess)
+ return -1;
+
+ return 1;
+}
+
+static int mac_shadow_capture_stop(macShadowSubsystem* subsystem)
+{
+ CGError err;
+ err = CGDisplayStreamStop(subsystem->stream);
+
+ if (err != kCGErrorSuccess)
+ return -1;
+
+ return 1;
+}
+
+static int mac_shadow_capture_get_dirty_region(macShadowSubsystem* subsystem)
+{
+ size_t numRects;
+ const CGRect* rects;
+ RECTANGLE_16 invalidRect;
+ rdpShadowSurface* surface = subsystem->common.server->surface;
+ rects = CGDisplayStreamUpdateGetRects(subsystem->lastUpdate, kCGDisplayStreamUpdateDirtyRects,
+ &numRects);
+
+ if (!numRects)
+ return -1;
+
+ for (size_t index = 0; index < numRects; index++)
+ {
+ invalidRect.left = (UINT16)rects[index].origin.x;
+ invalidRect.top = (UINT16)rects[index].origin.y;
+ invalidRect.right = invalidRect.left + (UINT16)rects[index].size.width;
+ invalidRect.bottom = invalidRect.top + (UINT16)rects[index].size.height;
+
+ if (subsystem->retina)
+ {
+ /* scale invalid rect */
+ invalidRect.left /= 2;
+ invalidRect.top /= 2;
+ invalidRect.right /= 2;
+ invalidRect.bottom /= 2;
+ }
+
+ region16_union_rect(&(surface->invalidRegion), &(surface->invalidRegion), &invalidRect);
+ }
+
+ return 0;
+}
+
+static int freerdp_image_copy_from_retina(BYTE* pDstData, DWORD DstFormat, int nDstStep, int nXDst,
+ int nYDst, int nWidth, int nHeight, BYTE* pSrcData,
+ int nSrcStep, int nXSrc, int nYSrc)
+{
+ BYTE* pSrcPixel;
+ BYTE* pDstPixel;
+ int nSrcPad;
+ int nDstPad;
+ int srcBitsPerPixel;
+ int srcBytesPerPixel;
+ int dstBitsPerPixel;
+ int dstBytesPerPixel;
+ srcBitsPerPixel = 24;
+ srcBytesPerPixel = 8;
+
+ if (nSrcStep < 0)
+ nSrcStep = srcBytesPerPixel * nWidth;
+
+ dstBitsPerPixel = FreeRDPGetBitsPerPixel(DstFormat);
+ dstBytesPerPixel = FreeRDPGetBytesPerPixel(DstFormat);
+
+ if (nDstStep < 0)
+ nDstStep = dstBytesPerPixel * nWidth;
+
+ nSrcPad = (nSrcStep - (nWidth * srcBytesPerPixel));
+ nDstPad = (nDstStep - (nWidth * dstBytesPerPixel));
+ pSrcPixel = &pSrcData[(nYSrc * nSrcStep) + (nXSrc * 4)];
+ pDstPixel = &pDstData[(nYDst * nDstStep) + (nXDst * 4)];
+
+ for (int y = 0; y < nHeight; y++)
+ {
+ for (int x = 0; x < nWidth; x++)
+ {
+ UINT32 R, G, B;
+ UINT32 color;
+ /* simple box filter scaling, could be improved with better algorithm */
+ B = pSrcPixel[0] + pSrcPixel[4] + pSrcPixel[nSrcStep + 0] + pSrcPixel[nSrcStep + 4];
+ G = pSrcPixel[1] + pSrcPixel[5] + pSrcPixel[nSrcStep + 1] + pSrcPixel[nSrcStep + 5];
+ R = pSrcPixel[2] + pSrcPixel[6] + pSrcPixel[nSrcStep + 2] + pSrcPixel[nSrcStep + 6];
+ pSrcPixel += 8;
+ color = FreeRDPGetColor(DstFormat, R >> 2, G >> 2, B >> 2, 0xFF);
+ FreeRDPWriteColor(pDstPixel, DstFormat, color);
+ pDstPixel += dstBytesPerPixel;
+ }
+
+ pSrcPixel = &pSrcPixel[nSrcPad + nSrcStep];
+ pDstPixel = &pDstPixel[nDstPad];
+ }
+
+ return 1;
+}
+
+static void (^mac_capture_stream_handler)(
+ CGDisplayStreamFrameStatus, uint64_t, IOSurfaceRef,
+ CGDisplayStreamUpdateRef) = ^(CGDisplayStreamFrameStatus status, uint64_t displayTime,
+ IOSurfaceRef frameSurface, CGDisplayStreamUpdateRef updateRef) {
+ int x, y;
+ int count;
+ int width;
+ int height;
+ int nSrcStep;
+ BOOL empty;
+ BYTE* pSrcData;
+ RECTANGLE_16 surfaceRect;
+ const RECTANGLE_16* extents;
+ macShadowSubsystem* subsystem = g_Subsystem;
+ rdpShadowServer* server = subsystem->common.server;
+ rdpShadowSurface* surface = server->surface;
+ count = ArrayList_Count(server->clients);
+
+ if (count < 1)
+ return;
+
+ EnterCriticalSection(&(surface->lock));
+ mac_shadow_capture_get_dirty_region(subsystem);
+ surfaceRect.left = 0;
+ surfaceRect.top = 0;
+ surfaceRect.right = surface->width;
+ surfaceRect.bottom = surface->height;
+ region16_intersect_rect(&(surface->invalidRegion), &(surface->invalidRegion), &surfaceRect);
+ empty = region16_is_empty(&(surface->invalidRegion));
+ LeaveCriticalSection(&(surface->lock));
+
+ if (!empty)
+ {
+ extents = region16_extents(&(surface->invalidRegion));
+ x = extents->left;
+ y = extents->top;
+ width = extents->right - extents->left;
+ height = extents->bottom - extents->top;
+ IOSurfaceLock(frameSurface, kIOSurfaceLockReadOnly, NULL);
+ pSrcData = (BYTE*)IOSurfaceGetBaseAddress(frameSurface);
+ nSrcStep = (int)IOSurfaceGetBytesPerRow(frameSurface);
+
+ if (subsystem->retina)
+ {
+ freerdp_image_copy_from_retina(surface->data, surface->format, surface->scanline, x, y,
+ width, height, pSrcData, nSrcStep, x, y);
+ }
+ else
+ {
+ freerdp_image_copy(surface->data, surface->format, surface->scanline, x, y, width, height,
+ pSrcData, PIXEL_FORMAT_BGRX32, nSrcStep, x, y, NULL,
+ FREERDP_FLIP_NONE);
+ }
+ LeaveCriticalSection(&(surface->lock));
+
+ IOSurfaceUnlock(frameSurface, kIOSurfaceLockReadOnly, NULL);
+ ArrayList_Lock(server->clients);
+ count = ArrayList_Count(server->clients);
+ shadow_subsystem_frame_update(&subsystem->common);
+
+ if (count == 1)
+ {
+ rdpShadowClient* client;
+ client = (rdpShadowClient*)ArrayList_GetItem(server->clients, 0);
+
+ if (client)
+ {
+ subsystem->common.captureFrameRate = shadow_encoder_preferred_fps(client->encoder);
+ }
+ }
+
+ ArrayList_Unlock(server->clients);
+ EnterCriticalSection(&(surface->lock));
+ region16_clear(&(surface->invalidRegion));
+ LeaveCriticalSection(&(surface->lock));
+ }
+
+ if (status != kCGDisplayStreamFrameStatusFrameComplete)
+ {
+ switch (status)
+ {
+ case kCGDisplayStreamFrameStatusFrameIdle:
+ break;
+
+ case kCGDisplayStreamFrameStatusStopped:
+ break;
+
+ case kCGDisplayStreamFrameStatusFrameBlank:
+ break;
+
+ default:
+ break;
+ }
+ }
+ else if (!subsystem->lastUpdate)
+ {
+ CFRetain(updateRef);
+ subsystem->lastUpdate = updateRef;
+ }
+ else
+ {
+ CGDisplayStreamUpdateRef tmpRef = subsystem->lastUpdate;
+ subsystem->lastUpdate = CGDisplayStreamUpdateCreateMergedUpdate(tmpRef, updateRef);
+ CFRelease(tmpRef);
+ }
+};
+
+static int mac_shadow_capture_init(macShadowSubsystem* subsystem)
+{
+ void* keys[2];
+ void* values[2];
+ CFDictionaryRef opts;
+ CGDirectDisplayID displayId;
+ displayId = CGMainDisplayID();
+ subsystem->captureQueue = dispatch_queue_create("mac.shadow.capture", NULL);
+ keys[0] = (void*)kCGDisplayStreamShowCursor;
+ values[0] = (void*)kCFBooleanFalse;
+ opts = CFDictionaryCreate(kCFAllocatorDefault, (const void**)keys, (const void**)values, 1,
+ NULL, NULL);
+ subsystem->stream = CGDisplayStreamCreateWithDispatchQueue(
+ displayId, subsystem->pixelWidth, subsystem->pixelHeight, 'BGRA', opts,
+ subsystem->captureQueue, mac_capture_stream_handler);
+ CFRelease(opts);
+ return 1;
+}
+
+static int mac_shadow_screen_grab(macShadowSubsystem* subsystem)
+{
+ return 1;
+}
+
+static int mac_shadow_subsystem_process_message(macShadowSubsystem* subsystem, wMessage* message)
+{
+ rdpShadowServer* server = subsystem->common.server;
+ rdpShadowSurface* surface = server->surface;
+
+ switch (message->id)
+ {
+ case SHADOW_MSG_IN_REFRESH_REQUEST_ID:
+ EnterCriticalSection(&(surface->lock));
+ shadow_subsystem_frame_update((rdpShadowSubsystem*)subsystem);
+ LeaveCriticalSection(&(surface->lock));
+ break;
+
+ default:
+ WLog_ERR(TAG, "Unknown message id: %" PRIu32 "", message->id);
+ break;
+ }
+
+ if (message->Free)
+ message->Free(message);
+
+ return 1;
+}
+
+static DWORD WINAPI mac_shadow_subsystem_thread(LPVOID arg)
+{
+ macShadowSubsystem* subsystem = (macShadowSubsystem*)arg;
+ DWORD status;
+ DWORD nCount;
+ UINT64 cTime;
+ DWORD dwTimeout;
+ DWORD dwInterval;
+ UINT64 frameTime;
+ HANDLE events[32];
+ wMessage message;
+ wMessagePipe* MsgPipe;
+ MsgPipe = subsystem->common.MsgPipe;
+ nCount = 0;
+ events[nCount++] = MessageQueue_Event(MsgPipe->In);
+ subsystem->common.captureFrameRate = 16;
+ dwInterval = 1000 / subsystem->common.captureFrameRate;
+ frameTime = GetTickCount64() + dwInterval;
+
+ while (1)
+ {
+ cTime = GetTickCount64();
+ dwTimeout = (cTime > frameTime) ? 0 : frameTime - cTime;
+ status = WaitForMultipleObjects(nCount, events, FALSE, dwTimeout);
+
+ if (WaitForSingleObject(MessageQueue_Event(MsgPipe->In), 0) == WAIT_OBJECT_0)
+ {
+ if (MessageQueue_Peek(MsgPipe->In, &message, TRUE))
+ {
+ if (message.id == WMQ_QUIT)
+ break;
+
+ mac_shadow_subsystem_process_message(subsystem, &message);
+ }
+ }
+
+ if ((status == WAIT_TIMEOUT) || (GetTickCount64() > frameTime))
+ {
+ mac_shadow_screen_grab(subsystem);
+ dwInterval = 1000 / subsystem->common.captureFrameRate;
+ frameTime += dwInterval;
+ }
+ }
+
+ ExitThread(0);
+ return 0;
+}
+
+static UINT32 mac_shadow_enum_monitors(MONITOR_DEF* monitors, UINT32 maxMonitors)
+{
+ int index;
+ size_t wide, high;
+ UINT32 numMonitors = 0;
+ MONITOR_DEF* monitor;
+ CGDirectDisplayID displayId;
+ displayId = CGMainDisplayID();
+ CGDisplayModeRef mode = CGDisplayCopyDisplayMode(displayId);
+ wide = CGDisplayPixelsWide(displayId);
+ high = CGDisplayPixelsHigh(displayId);
+ CGDisplayModeRelease(mode);
+ index = 0;
+ numMonitors = 1;
+ monitor = &monitors[index];
+ monitor->left = 0;
+ monitor->top = 0;
+ monitor->right = (int)wide;
+ monitor->bottom = (int)high;
+ monitor->flags = 1;
+ return numMonitors;
+}
+
+static int mac_shadow_subsystem_init(rdpShadowSubsystem* rdpsubsystem)
+{
+ macShadowSubsystem* subsystem = (macShadowSubsystem*)rdpsubsystem;
+ g_Subsystem = subsystem;
+
+ mac_shadow_detect_monitors(subsystem);
+ mac_shadow_capture_init(subsystem);
+ return 1;
+}
+
+static int mac_shadow_subsystem_uninit(rdpShadowSubsystem* rdpsubsystem)
+{
+ macShadowSubsystem* subsystem = (macShadowSubsystem*)rdpsubsystem;
+ if (!subsystem)
+ return -1;
+
+ if (subsystem->lastUpdate)
+ {
+ CFRelease(subsystem->lastUpdate);
+ subsystem->lastUpdate = NULL;
+ }
+
+ return 1;
+}
+
+static int mac_shadow_subsystem_start(rdpShadowSubsystem* rdpsubsystem)
+{
+ macShadowSubsystem* subsystem = (macShadowSubsystem*)rdpsubsystem;
+ HANDLE thread;
+
+ if (!subsystem)
+ return -1;
+
+ mac_shadow_capture_start(subsystem);
+
+ if (!(thread = CreateThread(NULL, 0, mac_shadow_subsystem_thread, (void*)subsystem, 0, NULL)))
+ {
+ WLog_ERR(TAG, "Failed to create thread");
+ return -1;
+ }
+
+ return 1;
+}
+
+static int mac_shadow_subsystem_stop(rdpShadowSubsystem* subsystem)
+{
+ if (!subsystem)
+ return -1;
+
+ return 1;
+}
+
+static void mac_shadow_subsystem_free(rdpShadowSubsystem* subsystem)
+{
+ if (!subsystem)
+ return;
+
+ mac_shadow_subsystem_uninit(subsystem);
+ free(subsystem);
+}
+
+static rdpShadowSubsystem* mac_shadow_subsystem_new(void)
+{
+ macShadowSubsystem* subsystem = calloc(1, sizeof(macShadowSubsystem));
+
+ if (!subsystem)
+ return NULL;
+
+ subsystem->common.SynchronizeEvent = mac_shadow_input_synchronize_event;
+ subsystem->common.KeyboardEvent = mac_shadow_input_keyboard_event;
+ subsystem->common.UnicodeKeyboardEvent = mac_shadow_input_unicode_keyboard_event;
+ subsystem->common.MouseEvent = mac_shadow_input_mouse_event;
+ subsystem->common.ExtendedMouseEvent = mac_shadow_input_extended_mouse_event;
+ return &subsystem->common;
+}
+
+FREERDP_API const char* ShadowSubsystemName(void)
+{
+ return "Mac";
+}
+
+FREERDP_API int ShadowSubsystemEntry(RDP_SHADOW_ENTRY_POINTS* pEntryPoints)
+{
+ char name[] = "mac shadow subsystem";
+ char* arg[] = { name };
+
+ freerdp_server_warn_unmaintained(ARRAYSIZE(arg), arg);
+ pEntryPoints->New = mac_shadow_subsystem_new;
+ pEntryPoints->Free = mac_shadow_subsystem_free;
+ pEntryPoints->Init = mac_shadow_subsystem_init;
+ pEntryPoints->Uninit = mac_shadow_subsystem_uninit;
+ pEntryPoints->Start = mac_shadow_subsystem_start;
+ pEntryPoints->Stop = mac_shadow_subsystem_stop;
+ pEntryPoints->EnumMonitors = mac_shadow_enum_monitors;
+ return 1;
+}
diff --git a/server/shadow/Mac/mac_shadow.h b/server/shadow/Mac/mac_shadow.h
new file mode 100644
index 0000000..2c31572
--- /dev/null
+++ b/server/shadow/Mac/mac_shadow.h
@@ -0,0 +1,64 @@
+/**
+ * FreeRDP: A Remote Desktop Protocol Implementation
+ *
+ * Copyright 2011-2014 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.
+ */
+
+#ifndef FREERDP_SERVER_SHADOW_MAC_SHADOW_H
+#define FREERDP_SERVER_SHADOW_MAC_SHADOW_H
+
+#include <freerdp/server/shadow.h>
+
+typedef struct mac_shadow_subsystem macShadowSubsystem;
+
+#include <winpr/crt.h>
+#include <winpr/synch.h>
+#include <winpr/thread.h>
+#include <winpr/stream.h>
+#include <winpr/collections.h>
+
+#include <dispatch/dispatch.h>
+#include <IOKit/IOKitLib.h>
+#include <IOSurface/IOSurface.h>
+#include <CoreVideo/CoreVideo.h>
+#include <CoreGraphics/CoreGraphics.h>
+
+struct mac_shadow_subsystem
+{
+ rdpShadowSubsystem common;
+
+ int width;
+ int height;
+ BOOL retina;
+ int pixelWidth;
+ int pixelHeight;
+ BOOL mouseDownLeft;
+ BOOL mouseDownRight;
+ BOOL mouseDownOther;
+ CGDisplayStreamRef stream;
+ dispatch_queue_t captureQueue;
+ CGDisplayStreamUpdateRef lastUpdate;
+};
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* FREERDP_SERVER_SHADOW_MAC_SHADOW_H */
diff --git a/server/shadow/Win/CMakeLists.txt b/server/shadow/Win/CMakeLists.txt
new file mode 100644
index 0000000..f8a920e
--- /dev/null
+++ b/server/shadow/Win/CMakeLists.txt
@@ -0,0 +1,20 @@
+
+include (WarnUnmaintained)
+warn_unmaintained("windows shadow server subsystem")
+
+add_definitions(-DWITH_SHADOW_WIN)
+add_library(freerdp-shadow-subsystem-impl STATIC
+ win_dxgi.c
+ win_dxgi.h
+ win_rdp.c
+ win_rdp.h
+ win_shadow.c
+ win_shadow.h
+ win_wds.c
+ win_wds.h
+)
+target_link_libraries(freerdp-shadow-subsystem-impl PRIVATE
+ freerdp-client
+ freerdp
+ winpr
+)
diff --git a/server/shadow/Win/win_dxgi.c b/server/shadow/Win/win_dxgi.c
new file mode 100644
index 0000000..3c25a00
--- /dev/null
+++ b/server/shadow/Win/win_dxgi.c
@@ -0,0 +1,794 @@
+/**
+ * FreeRDP: A Remote Desktop Protocol Implementation
+ *
+ * Copyright 2014 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.
+ */
+
+#include <freerdp/config.h>
+
+#include <winpr/crt.h>
+#include <winpr/print.h>
+#include <freerdp/log.h>
+
+#include "win_dxgi.h"
+
+#define TAG SERVER_TAG("shadow.win")
+
+#ifdef WITH_DXGI_1_2
+
+static D3D_DRIVER_TYPE DriverTypes[] = {
+ D3D_DRIVER_TYPE_HARDWARE,
+ D3D_DRIVER_TYPE_WARP,
+ D3D_DRIVER_TYPE_REFERENCE,
+};
+
+static UINT NumDriverTypes = ARRAYSIZE(DriverTypes);
+
+static D3D_FEATURE_LEVEL FeatureLevels[] = { D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_10_1,
+ D3D_FEATURE_LEVEL_10_0, D3D_FEATURE_LEVEL_9_1 };
+
+static UINT NumFeatureLevels = ARRAYSIZE(FeatureLevels);
+
+static HMODULE d3d11_module = NULL;
+
+typedef HRESULT(WINAPI* fnD3D11CreateDevice)(IDXGIAdapter* pAdapter, D3D_DRIVER_TYPE DriverType,
+ HMODULE Software, UINT Flags,
+ CONST D3D_FEATURE_LEVEL* pFeatureLevels,
+ UINT FeatureLevels, UINT SDKVersion,
+ ID3D11Device** ppDevice,
+ D3D_FEATURE_LEVEL* pFeatureLevel,
+ ID3D11DeviceContext** ppImmediateContext);
+
+static fnD3D11CreateDevice pfnD3D11CreateDevice = NULL;
+
+#undef DEFINE_GUID
+#define INITGUID
+
+#include <initguid.h>
+
+/* d3d11.h GUIDs */
+
+DEFINE_GUID(IID_ID3D11DeviceChild, 0x1841e5c8, 0x16b0, 0x489b, 0xbc, 0xc8, 0x44, 0xcf, 0xb0, 0xd5,
+ 0xde, 0xae);
+DEFINE_GUID(IID_ID3D11DepthStencilState, 0x03823efb, 0x8d8f, 0x4e1c, 0x9a, 0xa2, 0xf6, 0x4b, 0xb2,
+ 0xcb, 0xfd, 0xf1);
+DEFINE_GUID(IID_ID3D11BlendState, 0x75b68faa, 0x347d, 0x4159, 0x8f, 0x45, 0xa0, 0x64, 0x0f, 0x01,
+ 0xcd, 0x9a);
+DEFINE_GUID(IID_ID3D11RasterizerState, 0x9bb4ab81, 0xab1a, 0x4d8f, 0xb5, 0x06, 0xfc, 0x04, 0x20,
+ 0x0b, 0x6e, 0xe7);
+DEFINE_GUID(IID_ID3D11Resource, 0xdc8e63f3, 0xd12b, 0x4952, 0xb4, 0x7b, 0x5e, 0x45, 0x02, 0x6a,
+ 0x86, 0x2d);
+DEFINE_GUID(IID_ID3D11Buffer, 0x48570b85, 0xd1ee, 0x4fcd, 0xa2, 0x50, 0xeb, 0x35, 0x07, 0x22, 0xb0,
+ 0x37);
+DEFINE_GUID(IID_ID3D11Texture1D, 0xf8fb5c27, 0xc6b3, 0x4f75, 0xa4, 0xc8, 0x43, 0x9a, 0xf2, 0xef,
+ 0x56, 0x4c);
+DEFINE_GUID(IID_ID3D11Texture2D, 0x6f15aaf2, 0xd208, 0x4e89, 0x9a, 0xb4, 0x48, 0x95, 0x35, 0xd3,
+ 0x4f, 0x9c);
+DEFINE_GUID(IID_ID3D11Texture3D, 0x037e866e, 0xf56d, 0x4357, 0xa8, 0xaf, 0x9d, 0xab, 0xbe, 0x6e,
+ 0x25, 0x0e);
+DEFINE_GUID(IID_ID3D11View, 0x839d1216, 0xbb2e, 0x412b, 0xb7, 0xf4, 0xa9, 0xdb, 0xeb, 0xe0, 0x8e,
+ 0xd1);
+DEFINE_GUID(IID_ID3D11ShaderResourceView, 0xb0e06fe0, 0x8192, 0x4e1a, 0xb1, 0xca, 0x36, 0xd7, 0x41,
+ 0x47, 0x10, 0xb2);
+DEFINE_GUID(IID_ID3D11RenderTargetView, 0xdfdba067, 0x0b8d, 0x4865, 0x87, 0x5b, 0xd7, 0xb4, 0x51,
+ 0x6c, 0xc1, 0x64);
+DEFINE_GUID(IID_ID3D11DepthStencilView, 0x9fdac92a, 0x1876, 0x48c3, 0xaf, 0xad, 0x25, 0xb9, 0x4f,
+ 0x84, 0xa9, 0xb6);
+DEFINE_GUID(IID_ID3D11UnorderedAccessView, 0x28acf509, 0x7f5c, 0x48f6, 0x86, 0x11, 0xf3, 0x16, 0x01,
+ 0x0a, 0x63, 0x80);
+DEFINE_GUID(IID_ID3D11VertexShader, 0x3b301d64, 0xd678, 0x4289, 0x88, 0x97, 0x22, 0xf8, 0x92, 0x8b,
+ 0x72, 0xf3);
+DEFINE_GUID(IID_ID3D11HullShader, 0x8e5c6061, 0x628a, 0x4c8e, 0x82, 0x64, 0xbb, 0xe4, 0x5c, 0xb3,
+ 0xd5, 0xdd);
+DEFINE_GUID(IID_ID3D11DomainShader, 0xf582c508, 0x0f36, 0x490c, 0x99, 0x77, 0x31, 0xee, 0xce, 0x26,
+ 0x8c, 0xfa);
+DEFINE_GUID(IID_ID3D11GeometryShader, 0x38325b96, 0xeffb, 0x4022, 0xba, 0x02, 0x2e, 0x79, 0x5b,
+ 0x70, 0x27, 0x5c);
+DEFINE_GUID(IID_ID3D11PixelShader, 0xea82e40d, 0x51dc, 0x4f33, 0x93, 0xd4, 0xdb, 0x7c, 0x91, 0x25,
+ 0xae, 0x8c);
+DEFINE_GUID(IID_ID3D11ComputeShader, 0x4f5b196e, 0xc2bd, 0x495e, 0xbd, 0x01, 0x1f, 0xde, 0xd3, 0x8e,
+ 0x49, 0x69);
+DEFINE_GUID(IID_ID3D11InputLayout, 0xe4819ddc, 0x4cf0, 0x4025, 0xbd, 0x26, 0x5d, 0xe8, 0x2a, 0x3e,
+ 0x07, 0xb7);
+DEFINE_GUID(IID_ID3D11SamplerState, 0xda6fea51, 0x564c, 0x4487, 0x98, 0x10, 0xf0, 0xd0, 0xf9, 0xb4,
+ 0xe3, 0xa5);
+DEFINE_GUID(IID_ID3D11Asynchronous, 0x4b35d0cd, 0x1e15, 0x4258, 0x9c, 0x98, 0x1b, 0x13, 0x33, 0xf6,
+ 0xdd, 0x3b);
+DEFINE_GUID(IID_ID3D11Query, 0xd6c00747, 0x87b7, 0x425e, 0xb8, 0x4d, 0x44, 0xd1, 0x08, 0x56, 0x0a,
+ 0xfd);
+DEFINE_GUID(IID_ID3D11Predicate, 0x9eb576dd, 0x9f77, 0x4d86, 0x81, 0xaa, 0x8b, 0xab, 0x5f, 0xe4,
+ 0x90, 0xe2);
+DEFINE_GUID(IID_ID3D11Counter, 0x6e8c49fb, 0xa371, 0x4770, 0xb4, 0x40, 0x29, 0x08, 0x60, 0x22, 0xb7,
+ 0x41);
+DEFINE_GUID(IID_ID3D11ClassInstance, 0xa6cd7faa, 0xb0b7, 0x4a2f, 0x94, 0x36, 0x86, 0x62, 0xa6, 0x57,
+ 0x97, 0xcb);
+DEFINE_GUID(IID_ID3D11ClassLinkage, 0xddf57cba, 0x9543, 0x46e4, 0xa1, 0x2b, 0xf2, 0x07, 0xa0, 0xfe,
+ 0x7f, 0xed);
+DEFINE_GUID(IID_ID3D11CommandList, 0xa24bc4d1, 0x769e, 0x43f7, 0x80, 0x13, 0x98, 0xff, 0x56, 0x6c,
+ 0x18, 0xe2);
+DEFINE_GUID(IID_ID3D11DeviceContext, 0xc0bfa96c, 0xe089, 0x44fb, 0x8e, 0xaf, 0x26, 0xf8, 0x79, 0x61,
+ 0x90, 0xda);
+DEFINE_GUID(IID_ID3D11VideoDecoder, 0x3C9C5B51, 0x995D, 0x48d1, 0x9B, 0x8D, 0xFA, 0x5C, 0xAE, 0xDE,
+ 0xD6, 0x5C);
+DEFINE_GUID(IID_ID3D11VideoProcessorEnumerator, 0x31627037, 0x53AB, 0x4200, 0x90, 0x61, 0x05, 0xFA,
+ 0xA9, 0xAB, 0x45, 0xF9);
+DEFINE_GUID(IID_ID3D11VideoProcessor, 0x1D7B0652, 0x185F, 0x41c6, 0x85, 0xCE, 0x0C, 0x5B, 0xE3,
+ 0xD4, 0xAE, 0x6C);
+DEFINE_GUID(IID_ID3D11AuthenticatedChannel, 0x3015A308, 0xDCBD, 0x47aa, 0xA7, 0x47, 0x19, 0x24,
+ 0x86, 0xD1, 0x4D, 0x4A);
+DEFINE_GUID(IID_ID3D11CryptoSession, 0x9B32F9AD, 0xBDCC, 0x40a6, 0xA3, 0x9D, 0xD5, 0xC8, 0x65, 0x84,
+ 0x57, 0x20);
+DEFINE_GUID(IID_ID3D11VideoDecoderOutputView, 0xC2931AEA, 0x2A85, 0x4f20, 0x86, 0x0F, 0xFB, 0xA1,
+ 0xFD, 0x25, 0x6E, 0x18);
+DEFINE_GUID(IID_ID3D11VideoProcessorInputView, 0x11EC5A5F, 0x51DC, 0x4945, 0xAB, 0x34, 0x6E, 0x8C,
+ 0x21, 0x30, 0x0E, 0xA5);
+DEFINE_GUID(IID_ID3D11VideoProcessorOutputView, 0xA048285E, 0x25A9, 0x4527, 0xBD, 0x93, 0xD6, 0x8B,
+ 0x68, 0xC4, 0x42, 0x54);
+DEFINE_GUID(IID_ID3D11VideoContext, 0x61F21C45, 0x3C0E, 0x4a74, 0x9C, 0xEA, 0x67, 0x10, 0x0D, 0x9A,
+ 0xD5, 0xE4);
+DEFINE_GUID(IID_ID3D11VideoDevice, 0x10EC4D5B, 0x975A, 0x4689, 0xB9, 0xE4, 0xD0, 0xAA, 0xC3, 0x0F,
+ 0xE3, 0x33);
+DEFINE_GUID(IID_ID3D11Device, 0xdb6f6ddb, 0xac77, 0x4e88, 0x82, 0x53, 0x81, 0x9d, 0xf9, 0xbb, 0xf1,
+ 0x40);
+
+/* dxgi.h GUIDs */
+
+DEFINE_GUID(IID_IDXGIObject, 0xaec22fb8, 0x76f3, 0x4639, 0x9b, 0xe0, 0x28, 0xeb, 0x43, 0xa6, 0x7a,
+ 0x2e);
+DEFINE_GUID(IID_IDXGIDeviceSubObject, 0x3d3e0379, 0xf9de, 0x4d58, 0xbb, 0x6c, 0x18, 0xd6, 0x29,
+ 0x92, 0xf1, 0xa6);
+DEFINE_GUID(IID_IDXGIResource, 0x035f3ab4, 0x482e, 0x4e50, 0xb4, 0x1f, 0x8a, 0x7f, 0x8b, 0xd8, 0x96,
+ 0x0b);
+DEFINE_GUID(IID_IDXGIKeyedMutex, 0x9d8e1289, 0xd7b3, 0x465f, 0x81, 0x26, 0x25, 0x0e, 0x34, 0x9a,
+ 0xf8, 0x5d);
+DEFINE_GUID(IID_IDXGISurface, 0xcafcb56c, 0x6ac3, 0x4889, 0xbf, 0x47, 0x9e, 0x23, 0xbb, 0xd2, 0x60,
+ 0xec);
+DEFINE_GUID(IID_IDXGISurface1, 0x4AE63092, 0x6327, 0x4c1b, 0x80, 0xAE, 0xBF, 0xE1, 0x2E, 0xA3, 0x2B,
+ 0x86);
+DEFINE_GUID(IID_IDXGIAdapter, 0x2411e7e1, 0x12ac, 0x4ccf, 0xbd, 0x14, 0x97, 0x98, 0xe8, 0x53, 0x4d,
+ 0xc0);
+DEFINE_GUID(IID_IDXGIOutput, 0xae02eedb, 0xc735, 0x4690, 0x8d, 0x52, 0x5a, 0x8d, 0xc2, 0x02, 0x13,
+ 0xaa);
+DEFINE_GUID(IID_IDXGISwapChain, 0x310d36a0, 0xd2e7, 0x4c0a, 0xaa, 0x04, 0x6a, 0x9d, 0x23, 0xb8,
+ 0x88, 0x6a);
+DEFINE_GUID(IID_IDXGIFactory, 0x7b7166ec, 0x21c7, 0x44ae, 0xb2, 0x1a, 0xc9, 0xae, 0x32, 0x1a, 0xe3,
+ 0x69);
+DEFINE_GUID(IID_IDXGIDevice, 0x54ec77fa, 0x1377, 0x44e6, 0x8c, 0x32, 0x88, 0xfd, 0x5f, 0x44, 0xc8,
+ 0x4c);
+DEFINE_GUID(IID_IDXGIFactory1, 0x770aae78, 0xf26f, 0x4dba, 0xa8, 0x29, 0x25, 0x3c, 0x83, 0xd1, 0xb3,
+ 0x87);
+DEFINE_GUID(IID_IDXGIAdapter1, 0x29038f61, 0x3839, 0x4626, 0x91, 0xfd, 0x08, 0x68, 0x79, 0x01, 0x1a,
+ 0x05);
+DEFINE_GUID(IID_IDXGIDevice1, 0x77db970f, 0x6276, 0x48ba, 0xba, 0x28, 0x07, 0x01, 0x43, 0xb4, 0x39,
+ 0x2c);
+
+/* dxgi1_2.h GUIDs */
+
+DEFINE_GUID(IID_IDXGIDisplayControl, 0xea9dbf1a, 0xc88e, 0x4486, 0x85, 0x4a, 0x98, 0xaa, 0x01, 0x38,
+ 0xf3, 0x0c);
+DEFINE_GUID(IID_IDXGIOutputDuplication, 0x191cfac3, 0xa341, 0x470d, 0xb2, 0x6e, 0xa8, 0x64, 0xf4,
+ 0x28, 0x31, 0x9c);
+DEFINE_GUID(IID_IDXGISurface2, 0xaba496dd, 0xb617, 0x4cb8, 0xa8, 0x66, 0xbc, 0x44, 0xd7, 0xeb, 0x1f,
+ 0xa2);
+DEFINE_GUID(IID_IDXGIResource1, 0x30961379, 0x4609, 0x4a41, 0x99, 0x8e, 0x54, 0xfe, 0x56, 0x7e,
+ 0xe0, 0xc1);
+DEFINE_GUID(IID_IDXGIDevice2, 0x05008617, 0xfbfd, 0x4051, 0xa7, 0x90, 0x14, 0x48, 0x84, 0xb4, 0xf6,
+ 0xa9);
+DEFINE_GUID(IID_IDXGISwapChain1, 0x790a45f7, 0x0d42, 0x4876, 0x98, 0x3a, 0x0a, 0x55, 0xcf, 0xe6,
+ 0xf4, 0xaa);
+DEFINE_GUID(IID_IDXGIFactory2, 0x50c83a1c, 0xe072, 0x4c48, 0x87, 0xb0, 0x36, 0x30, 0xfa, 0x36, 0xa6,
+ 0xd0);
+DEFINE_GUID(IID_IDXGIAdapter2, 0x0AA1AE0A, 0xFA0E, 0x4B84, 0x86, 0x44, 0xE0, 0x5F, 0xF8, 0xE5, 0xAC,
+ 0xB5);
+DEFINE_GUID(IID_IDXGIOutput1, 0x00cddea8, 0x939b, 0x4b83, 0xa3, 0x40, 0xa6, 0x85, 0x22, 0x66, 0x66,
+ 0xcc);
+
+const char* GetDxgiErrorString(HRESULT hr)
+{
+ switch (hr)
+ {
+ case DXGI_STATUS_OCCLUDED:
+ return "DXGI_STATUS_OCCLUDED";
+ case DXGI_STATUS_CLIPPED:
+ return "DXGI_STATUS_CLIPPED";
+ case DXGI_STATUS_NO_REDIRECTION:
+ return "DXGI_STATUS_NO_REDIRECTION";
+ case DXGI_STATUS_NO_DESKTOP_ACCESS:
+ return "DXGI_STATUS_NO_DESKTOP_ACCESS";
+ case DXGI_STATUS_GRAPHICS_VIDPN_SOURCE_IN_USE:
+ return "DXGI_STATUS_GRAPHICS_VIDPN_SOURCE_IN_USE";
+ case DXGI_STATUS_MODE_CHANGED:
+ return "DXGI_STATUS_MODE_CHANGED";
+ case DXGI_STATUS_MODE_CHANGE_IN_PROGRESS:
+ return "DXGI_STATUS_MODE_CHANGE_IN_PROGRESS";
+ case DXGI_ERROR_INVALID_CALL:
+ return "DXGI_ERROR_INVALID_CALL";
+ case DXGI_ERROR_NOT_FOUND:
+ return "DXGI_ERROR_NOT_FOUND";
+ case DXGI_ERROR_MORE_DATA:
+ return "DXGI_ERROR_MORE_DATA";
+ case DXGI_ERROR_UNSUPPORTED:
+ return "DXGI_ERROR_UNSUPPORTED";
+ case DXGI_ERROR_DEVICE_REMOVED:
+ return "DXGI_ERROR_DEVICE_REMOVED";
+ case DXGI_ERROR_DEVICE_HUNG:
+ return "DXGI_ERROR_DEVICE_HUNG";
+ case DXGI_ERROR_DEVICE_RESET:
+ return "DXGI_ERROR_DEVICE_RESET";
+ case DXGI_ERROR_WAS_STILL_DRAWING:
+ return "DXGI_ERROR_WAS_STILL_DRAWING";
+ case DXGI_ERROR_FRAME_STATISTICS_DISJOINT:
+ return "DXGI_ERROR_FRAME_STATISTICS_DISJOINT";
+ case DXGI_ERROR_GRAPHICS_VIDPN_SOURCE_IN_USE:
+ return "DXGI_ERROR_GRAPHICS_VIDPN_SOURCE_IN_USE";
+ case DXGI_ERROR_DRIVER_INTERNAL_ERROR:
+ return "DXGI_ERROR_DRIVER_INTERNAL_ERROR";
+ case DXGI_ERROR_NONEXCLUSIVE:
+ return "DXGI_ERROR_NONEXCLUSIVE";
+ case DXGI_ERROR_NOT_CURRENTLY_AVAILABLE:
+ return "DXGI_ERROR_NOT_CURRENTLY_AVAILABLE";
+ case DXGI_ERROR_REMOTE_CLIENT_DISCONNECTED:
+ return "DXGI_ERROR_REMOTE_CLIENT_DISCONNECTED";
+ case DXGI_ERROR_REMOTE_OUTOFMEMORY:
+ return "DXGI_ERROR_REMOTE_OUTOFMEMORY";
+ case DXGI_ERROR_ACCESS_LOST:
+ return "DXGI_ERROR_ACCESS_LOST";
+ case DXGI_ERROR_WAIT_TIMEOUT:
+ return "DXGI_ERROR_WAIT_TIMEOUT";
+ case DXGI_ERROR_SESSION_DISCONNECTED:
+ return "DXGI_ERROR_SESSION_DISCONNECTED";
+ case DXGI_ERROR_RESTRICT_TO_OUTPUT_STALE:
+ return "DXGI_ERROR_RESTRICT_TO_OUTPUT_STALE";
+ case DXGI_ERROR_CANNOT_PROTECT_CONTENT:
+ return "DXGI_ERROR_CANNOT_PROTECT_CONTENT";
+ case DXGI_ERROR_ACCESS_DENIED:
+ return "DXGI_ERROR_ACCESS_DENIED";
+ case DXGI_ERROR_NAME_ALREADY_EXISTS:
+ return "DXGI_ERROR_NAME_ALREADY_EXISTS";
+ case DXGI_ERROR_SDK_COMPONENT_MISSING:
+ return "DXGI_ERROR_SDK_COMPONENT_MISSING";
+ case DXGI_STATUS_UNOCCLUDED:
+ return "DXGI_STATUS_UNOCCLUDED";
+ case DXGI_STATUS_DDA_WAS_STILL_DRAWING:
+ return "DXGI_STATUS_DDA_WAS_STILL_DRAWING";
+ case DXGI_ERROR_MODE_CHANGE_IN_PROGRESS:
+ return "DXGI_ERROR_MODE_CHANGE_IN_PROGRESS";
+ case DXGI_DDI_ERR_WASSTILLDRAWING:
+ return "DXGI_DDI_ERR_WASSTILLDRAWING";
+ case DXGI_DDI_ERR_UNSUPPORTED:
+ return "DXGI_DDI_ERR_UNSUPPORTED";
+ case DXGI_DDI_ERR_NONEXCLUSIVE:
+ return "DXGI_DDI_ERR_NONEXCLUSIVE";
+ case 0x80070005:
+ return "DXGI_ERROR_ACCESS_DENIED";
+ }
+
+ return "DXGI_ERROR_UNKNOWN";
+}
+
+static void win_shadow_d3d11_module_init()
+{
+ if (d3d11_module)
+ return;
+
+ d3d11_module = LoadLibraryA("d3d11.dll");
+
+ if (!d3d11_module)
+ return;
+
+ pfnD3D11CreateDevice = (fnD3D11CreateDevice)GetProcAddress(d3d11_module, "D3D11CreateDevice");
+}
+
+int win_shadow_dxgi_init_duplication(winShadowSubsystem* subsystem)
+{
+ HRESULT hr;
+ UINT dTop, i = 0;
+ IDXGIOutput* pOutput;
+ DXGI_OUTPUT_DESC outputDesc = { 0 };
+ DXGI_OUTPUT_DESC* pOutputDesc;
+ D3D11_TEXTURE2D_DESC textureDesc;
+ IDXGIDevice* dxgiDevice = NULL;
+ IDXGIAdapter* dxgiAdapter = NULL;
+ IDXGIOutput* dxgiOutput = NULL;
+ IDXGIOutput1* dxgiOutput1 = NULL;
+
+ hr = subsystem->dxgiDevice->lpVtbl->QueryInterface(subsystem->dxgiDevice, &IID_IDXGIDevice,
+ (void**)&dxgiDevice);
+
+ if (FAILED(hr))
+ {
+ WLog_ERR(TAG, "ID3D11Device::QueryInterface(IDXGIDevice) failure: %s (0x%08lX)",
+ GetDxgiErrorString(hr), hr);
+ return -1;
+ }
+
+ hr = dxgiDevice->lpVtbl->GetParent(dxgiDevice, &IID_IDXGIAdapter, (void**)&dxgiAdapter);
+
+ if (dxgiDevice)
+ {
+ dxgiDevice->lpVtbl->Release(dxgiDevice);
+ dxgiDevice = NULL;
+ }
+
+ if (FAILED(hr))
+ {
+ WLog_ERR(TAG, "IDXGIDevice::GetParent(IDXGIAdapter) failure: %s (0x%08lX)",
+ GetDxgiErrorString(hr), hr);
+ return -1;
+ }
+
+ pOutput = NULL;
+
+ while (dxgiAdapter->lpVtbl->EnumOutputs(dxgiAdapter, i, &pOutput) != DXGI_ERROR_NOT_FOUND)
+ {
+ pOutputDesc = &outputDesc;
+
+ hr = pOutput->lpVtbl->GetDesc(pOutput, pOutputDesc);
+
+ if (FAILED(hr))
+ {
+ WLog_ERR(TAG, "IDXGIOutput::GetDesc failure: %s (0x%08lX)", GetDxgiErrorString(hr), hr);
+ return -1;
+ }
+
+ if (pOutputDesc->AttachedToDesktop)
+ dTop = i;
+
+ pOutput->lpVtbl->Release(pOutput);
+ i++;
+ }
+
+ dTop = 0; /* screen id */
+
+ hr = dxgiAdapter->lpVtbl->EnumOutputs(dxgiAdapter, dTop, &dxgiOutput);
+
+ if (dxgiAdapter)
+ {
+ dxgiAdapter->lpVtbl->Release(dxgiAdapter);
+ dxgiAdapter = NULL;
+ }
+
+ if (FAILED(hr))
+ {
+ WLog_ERR(TAG, "IDXGIAdapter::EnumOutputs failure: %s (0x%08lX)", GetDxgiErrorString(hr),
+ hr);
+ return -1;
+ }
+
+ hr = dxgiOutput->lpVtbl->QueryInterface(dxgiOutput, &IID_IDXGIOutput1, (void**)&dxgiOutput1);
+
+ if (dxgiOutput)
+ {
+ dxgiOutput->lpVtbl->Release(dxgiOutput);
+ dxgiOutput = NULL;
+ }
+
+ if (FAILED(hr))
+ {
+ WLog_ERR(TAG, "IDXGIOutput::QueryInterface(IDXGIOutput1) failure: %s (0x%08lX)",
+ GetDxgiErrorString(hr), hr);
+ return -1;
+ }
+
+ hr = dxgiOutput1->lpVtbl->DuplicateOutput(dxgiOutput1, (IUnknown*)subsystem->dxgiDevice,
+ &(subsystem->dxgiOutputDuplication));
+
+ if (dxgiOutput1)
+ {
+ dxgiOutput1->lpVtbl->Release(dxgiOutput1);
+ dxgiOutput1 = NULL;
+ }
+
+ if (FAILED(hr))
+ {
+ WLog_ERR(TAG, "IDXGIOutput1::DuplicateOutput failure: %s (0x%08lX)", GetDxgiErrorString(hr),
+ hr);
+ return -1;
+ }
+
+ textureDesc.Width = subsystem->width;
+ textureDesc.Height = subsystem->height;
+ textureDesc.MipLevels = 1;
+ textureDesc.ArraySize = 1;
+ textureDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
+ textureDesc.SampleDesc.Count = 1;
+ textureDesc.SampleDesc.Quality = 0;
+ textureDesc.Usage = D3D11_USAGE_STAGING;
+ textureDesc.BindFlags = 0;
+ textureDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
+ textureDesc.MiscFlags = 0;
+
+ hr = subsystem->dxgiDevice->lpVtbl->CreateTexture2D(subsystem->dxgiDevice, &textureDesc, NULL,
+ &(subsystem->dxgiStage));
+
+ if (FAILED(hr))
+ {
+ WLog_ERR(TAG, "ID3D11Device::CreateTexture2D failure: %s (0x%08lX)", GetDxgiErrorString(hr),
+ hr);
+ return -1;
+ }
+
+ return 1;
+}
+
+int win_shadow_dxgi_init(winShadowSubsystem* subsystem)
+{
+ UINT i = 0;
+ HRESULT hr;
+ int status;
+ UINT DriverTypeIndex;
+ IDXGIDevice* DxgiDevice = NULL;
+ IDXGIAdapter* DxgiAdapter = NULL;
+ IDXGIOutput* DxgiOutput = NULL;
+ IDXGIOutput1* DxgiOutput1 = NULL;
+
+ win_shadow_d3d11_module_init();
+
+ if (!pfnD3D11CreateDevice)
+ return -1;
+
+ for (DriverTypeIndex = 0; DriverTypeIndex < NumDriverTypes; ++DriverTypeIndex)
+ {
+ hr = pfnD3D11CreateDevice(NULL, DriverTypes[DriverTypeIndex], NULL, 0, FeatureLevels,
+ NumFeatureLevels, D3D11_SDK_VERSION, &(subsystem->dxgiDevice),
+ &(subsystem->featureLevel), &(subsystem->dxgiDeviceContext));
+
+ if (SUCCEEDED(hr))
+ break;
+ }
+
+ if (FAILED(hr))
+ {
+ WLog_ERR(TAG, "D3D11CreateDevice failure: 0x%08lX", hr);
+ return -1;
+ }
+
+ status = win_shadow_dxgi_init_duplication(subsystem);
+
+ return status;
+}
+
+int win_shadow_dxgi_uninit(winShadowSubsystem* subsystem)
+{
+ if (subsystem->dxgiStage)
+ {
+ subsystem->dxgiStage->lpVtbl->Release(subsystem->dxgiStage);
+ subsystem->dxgiStage = NULL;
+ }
+
+ if (subsystem->dxgiDesktopImage)
+ {
+ subsystem->dxgiDesktopImage->lpVtbl->Release(subsystem->dxgiDesktopImage);
+ subsystem->dxgiDesktopImage = NULL;
+ }
+
+ if (subsystem->dxgiOutputDuplication)
+ {
+ subsystem->dxgiOutputDuplication->lpVtbl->Release(subsystem->dxgiOutputDuplication);
+ subsystem->dxgiOutputDuplication = NULL;
+ }
+
+ if (subsystem->dxgiDeviceContext)
+ {
+ subsystem->dxgiDeviceContext->lpVtbl->Release(subsystem->dxgiDeviceContext);
+ subsystem->dxgiDeviceContext = NULL;
+ }
+
+ if (subsystem->dxgiDevice)
+ {
+ subsystem->dxgiDevice->lpVtbl->Release(subsystem->dxgiDevice);
+ subsystem->dxgiDevice = NULL;
+ }
+
+ return 1;
+}
+
+int win_shadow_dxgi_fetch_frame_data(winShadowSubsystem* subsystem, BYTE** ppDstData,
+ int* pnDstStep, int x, int y, int width, int height)
+{
+ int status;
+ HRESULT hr;
+ D3D11_BOX Box;
+ DXGI_MAPPED_RECT mappedRect;
+
+ if ((width * height) < 1)
+ return 0;
+
+ Box.top = x;
+ Box.left = y;
+ Box.right = x + width;
+ Box.bottom = y + height;
+ Box.front = 0;
+ Box.back = 1;
+
+ subsystem->dxgiDeviceContext->lpVtbl->CopySubresourceRegion(
+ subsystem->dxgiDeviceContext, (ID3D11Resource*)subsystem->dxgiStage, 0, 0, 0, 0,
+ (ID3D11Resource*)subsystem->dxgiDesktopImage, 0, &Box);
+
+ hr = subsystem->dxgiStage->lpVtbl->QueryInterface(subsystem->dxgiStage, &IID_IDXGISurface,
+ (void**)&(subsystem->dxgiSurface));
+
+ if (FAILED(hr))
+ {
+ WLog_ERR(TAG, "ID3D11Texture2D::QueryInterface(IDXGISurface) failure: %s 0x%08lX",
+ GetDxgiErrorString(hr), hr);
+ return -1;
+ }
+
+ hr = subsystem->dxgiSurface->lpVtbl->Map(subsystem->dxgiSurface, &mappedRect, DXGI_MAP_READ);
+
+ if (FAILED(hr))
+ {
+ WLog_ERR(TAG, "IDXGISurface::Map failure: %s 0x%08lX", GetDxgiErrorString(hr), hr);
+
+ if (hr == DXGI_ERROR_DEVICE_REMOVED)
+ {
+ win_shadow_dxgi_uninit(subsystem);
+
+ status = win_shadow_dxgi_init(subsystem);
+
+ if (status < 0)
+ return -1;
+
+ return 0;
+ }
+
+ return -1;
+ }
+
+ subsystem->dxgiSurfaceMapped = TRUE;
+
+ *ppDstData = mappedRect.pBits;
+ *pnDstStep = mappedRect.Pitch;
+
+ return 1;
+}
+
+int win_shadow_dxgi_release_frame_data(winShadowSubsystem* subsystem)
+{
+ if (subsystem->dxgiSurface)
+ {
+ if (subsystem->dxgiSurfaceMapped)
+ {
+ subsystem->dxgiSurface->lpVtbl->Unmap(subsystem->dxgiSurface);
+ subsystem->dxgiSurfaceMapped = FALSE;
+ }
+
+ subsystem->dxgiSurface->lpVtbl->Release(subsystem->dxgiSurface);
+ subsystem->dxgiSurface = NULL;
+ }
+
+ if (subsystem->dxgiOutputDuplication)
+ {
+ if (subsystem->dxgiFrameAcquired)
+ {
+ subsystem->dxgiOutputDuplication->lpVtbl->ReleaseFrame(
+ subsystem->dxgiOutputDuplication);
+ subsystem->dxgiFrameAcquired = FALSE;
+ }
+ }
+
+ subsystem->pendingFrames = 0;
+
+ return 1;
+}
+
+int win_shadow_dxgi_get_next_frame(winShadowSubsystem* subsystem)
+{
+ UINT i = 0;
+ int status;
+ HRESULT hr = 0;
+ UINT timeout = 15;
+ UINT DataBufferSize = 0;
+ BYTE* DataBuffer = NULL;
+
+ if (subsystem->dxgiFrameAcquired)
+ {
+ win_shadow_dxgi_release_frame_data(subsystem);
+ }
+
+ if (subsystem->dxgiDesktopImage)
+ {
+ subsystem->dxgiDesktopImage->lpVtbl->Release(subsystem->dxgiDesktopImage);
+ subsystem->dxgiDesktopImage = NULL;
+ }
+
+ hr = subsystem->dxgiOutputDuplication->lpVtbl->AcquireNextFrame(
+ subsystem->dxgiOutputDuplication, timeout, &(subsystem->dxgiFrameInfo),
+ &(subsystem->dxgiResource));
+
+ if (SUCCEEDED(hr))
+ {
+ subsystem->dxgiFrameAcquired = TRUE;
+ subsystem->pendingFrames = subsystem->dxgiFrameInfo.AccumulatedFrames;
+ }
+
+ if (hr == DXGI_ERROR_WAIT_TIMEOUT)
+ return 0;
+
+ if (FAILED(hr))
+ {
+ WLog_ERR(TAG, "IDXGIOutputDuplication::AcquireNextFrame failure: %s (0x%08lX)",
+ GetDxgiErrorString(hr), hr);
+
+ if (hr == DXGI_ERROR_ACCESS_LOST)
+ {
+ win_shadow_dxgi_release_frame_data(subsystem);
+
+ if (subsystem->dxgiDesktopImage)
+ {
+ subsystem->dxgiDesktopImage->lpVtbl->Release(subsystem->dxgiDesktopImage);
+ subsystem->dxgiDesktopImage = NULL;
+ }
+
+ if (subsystem->dxgiOutputDuplication)
+ {
+ subsystem->dxgiOutputDuplication->lpVtbl->Release(subsystem->dxgiOutputDuplication);
+ subsystem->dxgiOutputDuplication = NULL;
+ }
+
+ status = win_shadow_dxgi_init_duplication(subsystem);
+
+ if (status < 0)
+ return -1;
+
+ return 0;
+ }
+ else if (hr == DXGI_ERROR_INVALID_CALL)
+ {
+ win_shadow_dxgi_uninit(subsystem);
+
+ status = win_shadow_dxgi_init(subsystem);
+
+ if (status < 0)
+ return -1;
+
+ return 0;
+ }
+
+ return -1;
+ }
+
+ hr = subsystem->dxgiResource->lpVtbl->QueryInterface(
+ subsystem->dxgiResource, &IID_ID3D11Texture2D, (void**)&(subsystem->dxgiDesktopImage));
+
+ if (subsystem->dxgiResource)
+ {
+ subsystem->dxgiResource->lpVtbl->Release(subsystem->dxgiResource);
+ subsystem->dxgiResource = NULL;
+ }
+
+ if (FAILED(hr))
+ {
+ WLog_ERR(TAG, "IDXGIResource::QueryInterface(ID3D11Texture2D) failure: %s (0x%08lX)",
+ GetDxgiErrorString(hr), hr);
+ return -1;
+ }
+
+ return 1;
+}
+
+int win_shadow_dxgi_get_invalid_region(winShadowSubsystem* subsystem)
+{
+ HRESULT hr;
+ POINT* pSrcPt;
+ RECT* pDstRect;
+ RECT* pDirtyRect;
+ UINT numMoveRects;
+ UINT numDirtyRects;
+ UINT UsedBufferSize;
+ RECTANGLE_16 invalidRect;
+ UINT MetadataBufferSize;
+ UINT MoveRectsBufferSize;
+ UINT DirtyRectsBufferSize;
+ RECT* pDirtyRectsBuffer;
+ DXGI_OUTDUPL_MOVE_RECT* pMoveRect;
+ DXGI_OUTDUPL_MOVE_RECT* pMoveRectBuffer;
+ rdpShadowSurface* surface = subsystem->server->surface;
+
+ if (subsystem->dxgiFrameInfo.AccumulatedFrames == 0)
+ return 0;
+
+ if (subsystem->dxgiFrameInfo.TotalMetadataBufferSize == 0)
+ return 0;
+
+ MetadataBufferSize = subsystem->dxgiFrameInfo.TotalMetadataBufferSize;
+
+ if (MetadataBufferSize > subsystem->MetadataBufferSize)
+ {
+ subsystem->MetadataBuffer = (BYTE*)realloc(subsystem->MetadataBuffer, MetadataBufferSize);
+
+ if (!subsystem->MetadataBuffer)
+ return -1;
+
+ subsystem->MetadataBufferSize = MetadataBufferSize;
+ }
+
+ /* GetFrameMoveRects */
+
+ UsedBufferSize = 0;
+
+ MoveRectsBufferSize = MetadataBufferSize - UsedBufferSize;
+ pMoveRectBuffer = (DXGI_OUTDUPL_MOVE_RECT*)&(subsystem->MetadataBuffer[UsedBufferSize]);
+
+ hr = subsystem->dxgiOutputDuplication->lpVtbl->GetFrameMoveRects(
+ subsystem->dxgiOutputDuplication, MoveRectsBufferSize, pMoveRectBuffer,
+ &MoveRectsBufferSize);
+
+ if (FAILED(hr))
+ {
+ WLog_ERR(TAG,
+ "IDXGIOutputDuplication::GetFrameMoveRects failure: %s (0x%08lX) Size: %u Total "
+ "%u Used: %u",
+ GetDxgiErrorString(hr), hr, MoveRectsBufferSize, MetadataBufferSize,
+ UsedBufferSize);
+ return -1;
+ }
+
+ /* GetFrameDirtyRects */
+
+ UsedBufferSize += MoveRectsBufferSize;
+
+ DirtyRectsBufferSize = MetadataBufferSize - UsedBufferSize;
+ pDirtyRectsBuffer = (RECT*)&(subsystem->MetadataBuffer[UsedBufferSize]);
+
+ hr = subsystem->dxgiOutputDuplication->lpVtbl->GetFrameDirtyRects(
+ subsystem->dxgiOutputDuplication, DirtyRectsBufferSize, pDirtyRectsBuffer,
+ &DirtyRectsBufferSize);
+
+ if (FAILED(hr))
+ {
+ WLog_ERR(TAG,
+ "IDXGIOutputDuplication::GetFrameDirtyRects failure: %s (0x%08lX) Size: %u Total "
+ "%u Used: %u",
+ GetDxgiErrorString(hr), hr, DirtyRectsBufferSize, MetadataBufferSize,
+ UsedBufferSize);
+ return -1;
+ }
+
+ numMoveRects = MoveRectsBufferSize / sizeof(DXGI_OUTDUPL_MOVE_RECT);
+
+ for (UINT i = 0; i < numMoveRects; i++)
+ {
+ pMoveRect = &pMoveRectBuffer[i];
+ pSrcPt = &(pMoveRect->SourcePoint);
+ pDstRect = &(pMoveRect->DestinationRect);
+
+ invalidRect.left = (UINT16)pDstRect->left;
+ invalidRect.top = (UINT16)pDstRect->top;
+ invalidRect.right = (UINT16)pDstRect->right;
+ invalidRect.bottom = (UINT16)pDstRect->bottom;
+
+ region16_union_rect(&(surface->invalidRegion), &(surface->invalidRegion), &invalidRect);
+ }
+
+ numDirtyRects = DirtyRectsBufferSize / sizeof(RECT);
+
+ for (UINT i = 0; i < numDirtyRects; i++)
+ {
+ pDirtyRect = &pDirtyRectsBuffer[i];
+
+ invalidRect.left = (UINT16)pDirtyRect->left;
+ invalidRect.top = (UINT16)pDirtyRect->top;
+ invalidRect.right = (UINT16)pDirtyRect->right;
+ invalidRect.bottom = (UINT16)pDirtyRect->bottom;
+
+ region16_union_rect(&(surface->invalidRegion), &(surface->invalidRegion), &invalidRect);
+ }
+
+ return 1;
+}
+
+#endif
diff --git a/server/shadow/Win/win_dxgi.h b/server/shadow/Win/win_dxgi.h
new file mode 100644
index 0000000..ffe80a0
--- /dev/null
+++ b/server/shadow/Win/win_dxgi.h
@@ -0,0 +1,61 @@
+/**
+ * FreeRDP: A Remote Desktop Protocol Implementation
+ *
+ * Copyright 2014 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.
+ */
+
+#ifndef FREERDP_SERVER_SHADOW_WIN_DXGI_H
+#define FREERDP_SERVER_SHADOW_WIN_DXGI_H
+
+#if _WIN32_WINNT >= 0x0602
+//#define WITH_DXGI_1_2 1
+#endif
+
+#ifdef WITH_DXGI_1_2
+
+#ifndef CINTERFACE
+#define CINTERFACE
+#endif
+
+#include <D3D11.h>
+#include <dxgi1_2.h>
+
+#endif
+
+#include "win_shadow.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#ifdef WITH_DXGI_1_2
+
+ int win_shadow_dxgi_init(winShadowSubsystem* subsystem);
+ int win_shadow_dxgi_uninit(winShadowSubsystem* subsystem);
+
+ int win_shadow_dxgi_fetch_frame_data(winShadowSubsystem* subsystem, BYTE** ppDstData,
+ int* pnDstStep, int x, int y, int width, int height);
+
+ int win_shadow_dxgi_get_next_frame(winShadowSubsystem* subsystem);
+ int win_shadow_dxgi_get_invalid_region(winShadowSubsystem* subsystem);
+
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* FREERDP_SERVER_SHADOW_WIN_DXGI_H */
diff --git a/server/shadow/Win/win_rdp.c b/server/shadow/Win/win_rdp.c
new file mode 100644
index 0000000..3db073d
--- /dev/null
+++ b/server/shadow/Win/win_rdp.c
@@ -0,0 +1,440 @@
+/**
+ * FreeRDP: A Remote Desktop Protocol Implementation
+ *
+ * Copyright 2014 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.
+ */
+
+#include <freerdp/config.h>
+
+#include <winpr/crt.h>
+#include <winpr/assert.h>
+#include <winpr/print.h>
+#include <winpr/assert.h>
+
+#include <freerdp/log.h>
+
+#include "win_rdp.h"
+
+#define TAG SERVER_TAG("shadow.win")
+
+static void shw_OnChannelConnectedEventHandler(void* context, const ChannelConnectedEventArgs* e)
+{
+ shwContext* shw = (shwContext*)context;
+ WINPR_ASSERT(e);
+ WLog_INFO(TAG, "OnChannelConnected: %s", e->name);
+}
+
+static void shw_OnChannelDisconnectedEventHandler(void* context,
+ const ChannelDisconnectedEventArgs* e)
+{
+ shwContext* shw = (shwContext*)context;
+ WINPR_ASSERT(e);
+ WLog_INFO(TAG, "OnChannelDisconnected: %s", e->name);
+}
+
+static BOOL shw_begin_paint(rdpContext* context)
+{
+ shwContext* shw;
+ rdpGdi* gdi;
+
+ WINPR_ASSERT(context);
+ gdi = context->gdi;
+ WINPR_ASSERT(gdi);
+ shw = (shwContext*)context;
+ gdi->primary->hdc->hwnd->invalid->null = TRUE;
+ gdi->primary->hdc->hwnd->ninvalid = 0;
+ return TRUE;
+}
+
+static BOOL shw_end_paint(rdpContext* context)
+{
+ int ninvalid;
+ HGDI_RGN cinvalid;
+ RECTANGLE_16 invalidRect;
+ rdpGdi* gdi = context->gdi;
+ shwContext* shw = (shwContext*)context;
+ winShadowSubsystem* subsystem = shw->subsystem;
+ rdpShadowSurface* surface = subsystem->base.server->surface;
+ ninvalid = gdi->primary->hdc->hwnd->ninvalid;
+ cinvalid = gdi->primary->hdc->hwnd->cinvalid;
+
+ for (int index = 0; index < ninvalid; index++)
+ {
+ invalidRect.left = cinvalid[index].x;
+ invalidRect.top = cinvalid[index].y;
+ invalidRect.right = cinvalid[index].x + cinvalid[index].w;
+ invalidRect.bottom = cinvalid[index].y + cinvalid[index].h;
+ region16_union_rect(&(surface->invalidRegion), &(surface->invalidRegion), &invalidRect);
+ }
+
+ SetEvent(subsystem->RdpUpdateEnterEvent);
+ WaitForSingleObject(subsystem->RdpUpdateLeaveEvent, INFINITE);
+ ResetEvent(subsystem->RdpUpdateLeaveEvent);
+ return TRUE;
+}
+
+BOOL shw_desktop_resize(rdpContext* context)
+{
+ WLog_WARN(TAG, "Desktop resizing not implemented!");
+ return TRUE;
+}
+
+static BOOL shw_surface_frame_marker(rdpContext* context,
+ const SURFACE_FRAME_MARKER* surfaceFrameMarker)
+{
+ shwContext* shw = (shwContext*)context;
+ return TRUE;
+}
+
+static BOOL shw_authenticate(freerdp* instance, char** username, char** password, char** domain)
+{
+ WLog_WARN(TAG, "Authentication not implemented, access granted to everyone!");
+ return TRUE;
+}
+
+static int shw_verify_x509_certificate(freerdp* instance, const BYTE* data, size_t length,
+ const char* hostname, UINT16 port, DWORD flags)
+{
+ WLog_WARN(TAG, "Certificate checks not implemented, access granted to everyone!");
+ return 1;
+}
+
+static void shw_OnConnectionResultEventHandler(void* context, const ConnectionResultEventArgs* e)
+{
+ shwContext* shw = (shwContext*)context;
+ WINPR_ASSERT(e);
+ WLog_INFO(TAG, "OnConnectionResult: %d", e->result);
+}
+
+static BOOL shw_pre_connect(freerdp* instance)
+{
+ shwContext* shw;
+ rdpContext* context = instance->context;
+ shw = (shwContext*)context;
+ PubSub_SubscribeConnectionResult(context->pubSub, shw_OnConnectionResultEventHandler);
+ PubSub_SubscribeChannelConnected(context->pubSub, shw_OnChannelConnectedEventHandler);
+ PubSub_SubscribeChannelDisconnected(context->pubSub, shw_OnChannelDisconnectedEventHandler);
+
+ return TRUE;
+}
+
+static BOOL shw_post_connect(freerdp* instance)
+{
+ rdpGdi* gdi;
+ shwContext* shw;
+ rdpUpdate* update;
+ rdpSettings* settings;
+
+ WINPR_ASSERT(instance);
+
+ shw = (shwContext*)instance->context;
+ WINPR_ASSERT(shw);
+
+ update = instance->context->update;
+ WINPR_ASSERT(update);
+
+ settings = instance->context->settings;
+ WINPR_ASSERT(settings);
+
+ if (!gdi_init(instance, PIXEL_FORMAT_BGRX32))
+ return FALSE;
+
+ gdi = instance->context->gdi;
+ update->BeginPaint = shw_begin_paint;
+ update->EndPaint = shw_end_paint;
+ update->DesktopResize = shw_desktop_resize;
+ update->SurfaceFrameMarker = shw_surface_frame_marker;
+ return TRUE;
+}
+
+static DWORD WINAPI shw_client_thread(LPVOID arg)
+{
+ int index;
+ BOOL bSuccess;
+ shwContext* shw;
+ rdpContext* context;
+ rdpChannels* channels;
+
+ freerdp* instance = (freerdp*)arg;
+ WINPR_ASSERT(instance);
+
+ context = (rdpContext*)instance->context;
+ WINPR_ASSERT(context);
+
+ shw = (shwContext*)context;
+
+ bSuccess = freerdp_connect(instance);
+ WLog_INFO(TAG, "freerdp_connect: %d", bSuccess);
+
+ if (!bSuccess)
+ {
+ ExitThread(0);
+ return 0;
+ }
+
+ channels = context->channels;
+ WINPR_ASSERT(channels);
+
+ while (1)
+ {
+ DWORD status;
+ HANDLE handles[MAXIMUM_WAIT_OBJECTS] = { 0 };
+ DWORD count = freerdp_get_event_handles(instance->context, handles, ARRAYSIZE(handles));
+
+ if ((count == 0) || (count == MAXIMUM_WAIT_OBJECTS))
+ {
+ WLog_ERR(TAG, "Failed to get FreeRDP event handles");
+ break;
+ }
+
+ handles[count++] = freerdp_channels_get_event_handle(instance);
+
+ if (MsgWaitForMultipleObjects(count, handles, FALSE, 1000, QS_ALLINPUT) == WAIT_FAILED)
+ {
+ WLog_ERR(TAG, "MsgWaitForMultipleObjects failure: 0x%08lX", GetLastError());
+ break;
+ }
+
+ if (!freerdp_check_fds(instance))
+ {
+ WLog_ERR(TAG, "Failed to check FreeRDP file descriptor");
+ break;
+ }
+
+ if (freerdp_shall_disconnect_context(instance->context))
+ {
+ break;
+ }
+
+ if (!freerdp_channels_check_fds(channels, instance))
+ {
+ WLog_ERR(TAG, "Failed to check channels file descriptor");
+ break;
+ }
+ }
+
+ freerdp_free(instance);
+ ExitThread(0);
+ return 0;
+}
+
+/**
+ * Client Interface
+ */
+
+static BOOL shw_freerdp_client_global_init(void)
+{
+ return TRUE;
+}
+
+static void shw_freerdp_client_global_uninit(void)
+{
+}
+
+static int shw_freerdp_client_start(rdpContext* context)
+{
+ shwContext* shw;
+ freerdp* instance = context->instance;
+ shw = (shwContext*)context;
+
+ if (!(shw->common.thread = CreateThread(NULL, 0, shw_client_thread, instance, 0, NULL)))
+ {
+ WLog_ERR(TAG, "Failed to create thread");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int shw_freerdp_client_stop(rdpContext* context)
+{
+ shwContext* shw = (shwContext*)context;
+ SetEvent(shw->StopEvent);
+ return 0;
+}
+
+static BOOL shw_freerdp_client_new(freerdp* instance, rdpContext* context)
+{
+ shwContext* shw;
+ rdpSettings* settings;
+
+ WINPR_ASSERT(instance);
+ WINPR_ASSERT(context);
+
+ shw = (shwContext*)instance->context;
+ WINPR_ASSERT(shw);
+
+ if (!(shw->StopEvent = CreateEvent(NULL, TRUE, FALSE, NULL)))
+ return FALSE;
+
+ instance->LoadChannels = freerdp_client_load_channels;
+ instance->PreConnect = shw_pre_connect;
+ instance->PostConnect = shw_post_connect;
+ instance->Authenticate = shw_authenticate;
+ instance->VerifyX509Certificate = shw_verify_x509_certificate;
+
+ settings = context->settings;
+ WINPR_ASSERT(settings);
+
+ shw->settings = settings;
+ if (!freerdp_settings_set_bool(settings, FreeRDP_AsyncChannels, FALSE))
+ return FALSE;
+ if (!freerdp_settings_set_bool(settings, FreeRDP_AsyncUpdate, FALSE))
+ return FALSE;
+ if (!freerdp_settings_set_bool(settings, FreeRDP_IgnoreCertificate, TRUE))
+ return FALSE;
+ if (!freerdp_settings_set_bool(settings, FreeRDP_ExternalCertificateManagement, TRUE))
+ return FALSE;
+ if (!freerdp_settings_set_bool(settings, FreeRDP_RdpSecurity, TRUE))
+ return FALSE;
+ if (!freerdp_settings_set_bool(settings, FreeRDP_TlsSecurity, TRUE))
+ return FALSE;
+ if (!freerdp_settings_set_bool(settings, FreeRDP_NlaSecurity, FALSE))
+ return FALSE;
+ if (!freerdp_settings_set_bool(settings, FreeRDP_BitmapCacheEnabled, FALSE))
+ return FALSE;
+ if (!freerdp_settings_set_bool(settings, FreeRDP_BitmapCacheV3Enabled, FALSE))
+ return FALSE;
+ if (!freerdp_settings_set_bool(settings, FreeRDP_OffscreenSupportLevel, FALSE))
+ return FALSE;
+ if (!freerdp_settings_set_uint32(settings, FreeRDP_GlyphSupportLevel, GLYPH_SUPPORT_NONE))
+ return FALSE;
+ if (!freerdp_settings_set_bool(settings, FreeRDP_BrushSupportLevel, FALSE))
+ return FALSE;
+ ZeroMemory(freerdp_settings_get_pointer_writable(settings, FreeRDP_OrderSupport), 32);
+ if (!freerdp_settings_set_bool(settings, FreeRDP_FrameMarkerCommandEnabled, TRUE))
+ return FALSE;
+ if (!freerdp_settings_set_bool(settings, FreeRDP_SurfaceFrameMarkerEnabled, TRUE))
+ return FALSE;
+ if (!freerdp_settings_set_bool(settings, FreeRDP_AltSecFrameMarkerSupport, TRUE))
+ return FALSE;
+ if (!freerdp_settings_set_uint32(settings, FreeRDP_ColorDepth, 32))
+ return FALSE;
+ if (!freerdp_settings_set_bool(settings, FreeRDP_NSCodec, TRUE))
+ return FALSE;
+ if (!freerdp_settings_set_bool(settings, FreeRDP_RemoteFxCodec, TRUE))
+ return FALSE;
+ if (!freerdp_settings_set_bool(settings, FreeRDP_FastPathInput, TRUE))
+ return FALSE;
+ if (!freerdp_settings_set_bool(settings, FreeRDP_FastPathOutput, TRUE))
+ return FALSE;
+ if (!freerdp_settings_set_bool(settings, FreeRDP_LargePointerFlag, TRUE))
+ return FALSE;
+ if (!freerdp_settings_set_bool(settings, FreeRDP_CompressionEnabled, FALSE))
+ return FALSE;
+ if (!freerdp_settings_set_bool(settings, FreeRDP_AutoReconnectionEnabled, FALSE))
+ return FALSE;
+ if (!freerdp_settings_set_bool(settings, FreeRDP_NetworkAutoDetect, FALSE))
+ return FALSE;
+ if (!freerdp_settings_set_bool(settings, FreeRDP_SupportHeartbeatPdu, FALSE))
+ return FALSE;
+ if (!freerdp_settings_set_bool(settings, FreeRDP_SupportMultitransport, FALSE))
+ return FALSE;
+ if (!freerdp_settings_set_uint32(settings, FreeRDP_ConnectionType, CONNECTION_TYPE_LAN))
+ return FALSE;
+ if (!freerdp_settings_set_bool(settings, FreeRDP_AllowFontSmoothing, TRUE))
+ return FALSE;
+ if (!freerdp_settings_set_bool(settings, FreeRDP_AllowDesktopComposition, TRUE))
+ return FALSE;
+ if (!freerdp_settings_set_bool(settings, FreeRDP_DisableWallpaper, FALSE))
+ return FALSE;
+ if (!freerdp_settings_set_bool(settings, FreeRDP_DisableFullWindowDrag, TRUE))
+ return FALSE;
+ if (!freerdp_settings_set_bool(settings, FreeRDP_DisableMenuAnims, TRUE))
+ return FALSE;
+ if (!freerdp_settings_set_bool(settings, FreeRDP_DisableThemes, FALSE))
+ return FALSE;
+ if (!freerdp_settings_set_bool(settings, FreeRDP_DeviceRedirection, TRUE))
+ return FALSE;
+ if (!freerdp_settings_set_bool(settings, FreeRDP_RedirectClipboard, TRUE))
+ return FALSE;
+ if (!freerdp_settings_set_bool(settings, FreeRDP_SupportDynamicChannels, TRUE))
+ return FALSE;
+ return TRUE;
+}
+
+static void shw_freerdp_client_free(freerdp* instance, rdpContext* context)
+{
+ shwContext* shw = (shwContext*)instance->context;
+}
+
+int shw_RdpClientEntry(RDP_CLIENT_ENTRY_POINTS* pEntryPoints)
+{
+ pEntryPoints->Version = 1;
+ pEntryPoints->Size = sizeof(RDP_CLIENT_ENTRY_POINTS_V1);
+ pEntryPoints->settings = NULL;
+ pEntryPoints->ContextSize = sizeof(shwContext);
+ pEntryPoints->GlobalInit = shw_freerdp_client_global_init;
+ pEntryPoints->GlobalUninit = shw_freerdp_client_global_uninit;
+ pEntryPoints->ClientNew = shw_freerdp_client_new;
+ pEntryPoints->ClientFree = shw_freerdp_client_free;
+ pEntryPoints->ClientStart = shw_freerdp_client_start;
+ pEntryPoints->ClientStop = shw_freerdp_client_stop;
+ return 0;
+}
+
+int win_shadow_rdp_init(winShadowSubsystem* subsystem)
+{
+ rdpContext* context;
+ RDP_CLIENT_ENTRY_POINTS clientEntryPoints = { 0 };
+
+ clientEntryPoints.Size = sizeof(RDP_CLIENT_ENTRY_POINTS);
+ clientEntryPoints.Version = RDP_CLIENT_INTERFACE_VERSION;
+ shw_RdpClientEntry(&clientEntryPoints);
+
+ if (!(subsystem->RdpUpdateEnterEvent = CreateEvent(NULL, TRUE, FALSE, NULL)))
+ goto fail_enter_event;
+
+ if (!(subsystem->RdpUpdateLeaveEvent = CreateEvent(NULL, TRUE, FALSE, NULL)))
+ goto fail_leave_event;
+
+ if (!(context = freerdp_client_context_new(&clientEntryPoints)))
+ goto fail_context;
+
+ subsystem->shw = (shwContext*)context;
+ subsystem->shw->settings = context->settings;
+ subsystem->shw->subsystem = subsystem;
+ return 1;
+fail_context:
+ CloseHandle(subsystem->RdpUpdateLeaveEvent);
+fail_leave_event:
+ CloseHandle(subsystem->RdpUpdateEnterEvent);
+fail_enter_event:
+ return -1;
+}
+
+int win_shadow_rdp_start(winShadowSubsystem* subsystem)
+{
+ int status;
+ shwContext* shw = subsystem->shw;
+ rdpContext* context = (rdpContext*)shw;
+ status = freerdp_client_start(context);
+ return status;
+}
+
+int win_shadow_rdp_stop(winShadowSubsystem* subsystem)
+{
+ int status;
+ shwContext* shw = subsystem->shw;
+ rdpContext* context = (rdpContext*)shw;
+ status = freerdp_client_stop(context);
+ return status;
+}
+
+int win_shadow_rdp_uninit(winShadowSubsystem* subsystem)
+{
+ win_shadow_rdp_stop(subsystem);
+ return 1;
+}
diff --git a/server/shadow/Win/win_rdp.h b/server/shadow/Win/win_rdp.h
new file mode 100644
index 0000000..07dbf3b
--- /dev/null
+++ b/server/shadow/Win/win_rdp.h
@@ -0,0 +1,56 @@
+/**
+ * FreeRDP: A Remote Desktop Protocol Implementation
+ *
+ * Copyright 2014 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.
+ */
+
+#ifndef FREERDP_SERVER_SHADOW_WIN_RDP_H
+#define FREERDP_SERVER_SHADOW_WIN_RDP_H
+
+#include <freerdp/addin.h>
+#include <freerdp/gdi/gdi.h>
+#include <freerdp/client/cmdline.h>
+#include <freerdp/channels/channels.h>
+
+typedef struct shw_context shwContext;
+
+#include "win_shadow.h"
+
+struct shw_context
+{
+ rdpClientContext common;
+
+ HANDLE StopEvent;
+ freerdp* instance;
+ rdpSettings* settings;
+ winShadowSubsystem* subsystem;
+};
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+ int win_shadow_rdp_init(winShadowSubsystem* subsystem);
+ int win_shadow_rdp_uninit(winShadowSubsystem* subsystem);
+
+ int win_shadow_rdp_start(winShadowSubsystem* subsystem);
+ int win_shadow_rdp_stop(winShadowSubsystem* subsystem);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* FREERDP_SERVER_SHADOW_WIN_RDP_H */
diff --git a/server/shadow/Win/win_shadow.c b/server/shadow/Win/win_shadow.c
new file mode 100644
index 0000000..d3d5a17
--- /dev/null
+++ b/server/shadow/Win/win_shadow.c
@@ -0,0 +1,560 @@
+/**
+ * FreeRDP: A Remote Desktop Protocol Implementation
+ *
+ * Copyright 2011-2014 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.
+ */
+
+#include <windows.h>
+
+#include <winpr/crt.h>
+#include <winpr/synch.h>
+#include <winpr/sysinfo.h>
+
+#include <freerdp/log.h>
+#include <freerdp/codec/color.h>
+#include <freerdp/codec/region.h>
+#include <freerdp/server/server-common.h>
+
+#include "win_shadow.h"
+
+#define TAG SERVER_TAG("shadow.win")
+
+/* https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-mouse_event
+ * does not mention this flag is only supported if building for _WIN32_WINNT >= 0x0600
+ */
+#ifndef MOUSEEVENTF_HWHEEL
+#define MOUSEEVENTF_HWHEEL 0x1000
+#endif
+
+static BOOL win_shadow_input_synchronize_event(rdpShadowSubsystem* subsystem,
+ rdpShadowClient* client, UINT32 flags)
+{
+ WLog_WARN(TAG, "TODO: Implement!");
+ return TRUE;
+}
+
+static BOOL win_shadow_input_keyboard_event(rdpShadowSubsystem* subsystem, rdpShadowClient* client,
+ UINT16 flags, UINT8 code)
+{
+ UINT rc;
+ INPUT event;
+ event.type = INPUT_KEYBOARD;
+ event.ki.wVk = 0;
+ event.ki.wScan = code;
+ event.ki.dwFlags = KEYEVENTF_SCANCODE;
+ event.ki.dwExtraInfo = 0;
+ event.ki.time = 0;
+
+ if (flags & KBD_FLAGS_RELEASE)
+ event.ki.dwFlags |= KEYEVENTF_KEYUP;
+
+ if (flags & KBD_FLAGS_EXTENDED)
+ event.ki.dwFlags |= KEYEVENTF_EXTENDEDKEY;
+
+ rc = SendInput(1, &event, sizeof(INPUT));
+ if (rc == 0)
+ return FALSE;
+ return TRUE;
+}
+
+static BOOL win_shadow_input_unicode_keyboard_event(rdpShadowSubsystem* subsystem,
+ rdpShadowClient* client, UINT16 flags,
+ UINT16 code)
+{
+ UINT rc;
+ INPUT event;
+ event.type = INPUT_KEYBOARD;
+ event.ki.wVk = 0;
+ event.ki.wScan = code;
+ event.ki.dwFlags = KEYEVENTF_UNICODE;
+ event.ki.dwExtraInfo = 0;
+ event.ki.time = 0;
+
+ if (flags & KBD_FLAGS_RELEASE)
+ event.ki.dwFlags |= KEYEVENTF_KEYUP;
+
+ rc = SendInput(1, &event, sizeof(INPUT));
+ if (rc == 0)
+ return FALSE;
+ return TRUE;
+}
+
+static BOOL win_shadow_input_mouse_event(rdpShadowSubsystem* subsystem, rdpShadowClient* client,
+ UINT16 flags, UINT16 x, UINT16 y)
+{
+ UINT rc = 1;
+ INPUT event = { 0 };
+ float width;
+ float height;
+
+ event.type = INPUT_MOUSE;
+
+ if (flags & (PTR_FLAGS_WHEEL | PTR_FLAGS_HWHEEL))
+ {
+ if (flags & PTR_FLAGS_WHEEL)
+ event.mi.dwFlags = MOUSEEVENTF_WHEEL;
+ else
+ event.mi.dwFlags = MOUSEEVENTF_HWHEEL;
+ event.mi.mouseData = flags & WheelRotationMask;
+
+ if (flags & PTR_FLAGS_WHEEL_NEGATIVE)
+ event.mi.mouseData *= -1;
+
+ rc = SendInput(1, &event, sizeof(INPUT));
+
+ /* The build target is a system that did not support MOUSEEVENTF_HWHEEL
+ * but it may run on newer systems supporting it.
+ * Ignore the return value in these cases.
+ */
+#if (_WIN32_WINNT < 0x0600)
+ if (flags & PTR_FLAGS_HWHEEL)
+ rc = 1;
+#endif
+ }
+ else
+ {
+ width = (float)GetSystemMetrics(SM_CXSCREEN);
+ height = (float)GetSystemMetrics(SM_CYSCREEN);
+ event.mi.dx = (LONG)((float)x * (65535.0f / width));
+ event.mi.dy = (LONG)((float)y * (65535.0f / height));
+ event.mi.dwFlags = MOUSEEVENTF_ABSOLUTE;
+
+ if (flags & PTR_FLAGS_MOVE)
+ {
+ event.mi.dwFlags |= MOUSEEVENTF_MOVE;
+ rc = SendInput(1, &event, sizeof(INPUT));
+ if (rc == 0)
+ return FALSE;
+ }
+
+ event.mi.dwFlags = MOUSEEVENTF_ABSOLUTE;
+
+ if (flags & PTR_FLAGS_BUTTON1)
+ {
+ if (flags & PTR_FLAGS_DOWN)
+ event.mi.dwFlags |= MOUSEEVENTF_LEFTDOWN;
+ else
+ event.mi.dwFlags |= MOUSEEVENTF_LEFTUP;
+
+ rc = SendInput(1, &event, sizeof(INPUT));
+ }
+ else if (flags & PTR_FLAGS_BUTTON2)
+ {
+ if (flags & PTR_FLAGS_DOWN)
+ event.mi.dwFlags |= MOUSEEVENTF_RIGHTDOWN;
+ else
+ event.mi.dwFlags |= MOUSEEVENTF_RIGHTUP;
+
+ rc = SendInput(1, &event, sizeof(INPUT));
+ }
+ else if (flags & PTR_FLAGS_BUTTON3)
+ {
+ if (flags & PTR_FLAGS_DOWN)
+ event.mi.dwFlags |= MOUSEEVENTF_MIDDLEDOWN;
+ else
+ event.mi.dwFlags |= MOUSEEVENTF_MIDDLEUP;
+
+ rc = SendInput(1, &event, sizeof(INPUT));
+ }
+ }
+
+ if (rc == 0)
+ return FALSE;
+ return TRUE;
+}
+
+static BOOL win_shadow_input_extended_mouse_event(rdpShadowSubsystem* subsystem,
+ rdpShadowClient* client, UINT16 flags, UINT16 x,
+ UINT16 y)
+{
+ UINT rc = 1;
+ INPUT event = { 0 };
+ float width;
+ float height;
+
+ if ((flags & PTR_XFLAGS_BUTTON1) || (flags & PTR_XFLAGS_BUTTON2))
+ {
+ event.type = INPUT_MOUSE;
+
+ if (flags & PTR_FLAGS_MOVE)
+ {
+ width = (float)GetSystemMetrics(SM_CXSCREEN);
+ height = (float)GetSystemMetrics(SM_CYSCREEN);
+ event.mi.dx = (LONG)((float)x * (65535.0f / width));
+ event.mi.dy = (LONG)((float)y * (65535.0f / height));
+ event.mi.dwFlags = MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE;
+ rc = SendInput(1, &event, sizeof(INPUT));
+ if (rc == 0)
+ return FALSE;
+ }
+
+ event.mi.dx = event.mi.dy = event.mi.dwFlags = 0;
+
+ if (flags & PTR_XFLAGS_DOWN)
+ event.mi.dwFlags |= MOUSEEVENTF_XDOWN;
+ else
+ event.mi.dwFlags |= MOUSEEVENTF_XUP;
+
+ if (flags & PTR_XFLAGS_BUTTON1)
+ event.mi.mouseData = XBUTTON1;
+ else if (flags & PTR_XFLAGS_BUTTON2)
+ event.mi.mouseData = XBUTTON2;
+
+ rc = SendInput(1, &event, sizeof(INPUT));
+ }
+
+ if (rc == 0)
+ return FALSE;
+ return TRUE;
+}
+
+static int win_shadow_invalidate_region(winShadowSubsystem* subsystem, int x, int y, int width,
+ int height)
+{
+ rdpShadowServer* server;
+ rdpShadowSurface* surface;
+ RECTANGLE_16 invalidRect;
+ server = subsystem->base.server;
+ surface = server->surface;
+ invalidRect.left = x;
+ invalidRect.top = y;
+ invalidRect.right = x + width;
+ invalidRect.bottom = y + height;
+ EnterCriticalSection(&(surface->lock));
+ region16_union_rect(&(surface->invalidRegion), &(surface->invalidRegion), &invalidRect);
+ LeaveCriticalSection(&(surface->lock));
+ return 1;
+}
+
+static int win_shadow_surface_copy(winShadowSubsystem* subsystem)
+{
+ int x, y;
+ int width;
+ int height;
+ int count;
+ int status = 1;
+ int nDstStep = 0;
+ DWORD DstFormat;
+ BYTE* pDstData = NULL;
+ rdpShadowServer* server;
+ rdpShadowSurface* surface;
+ RECTANGLE_16 surfaceRect;
+ RECTANGLE_16 invalidRect;
+ const RECTANGLE_16* extents;
+ server = subsystem->base.server;
+ surface = server->surface;
+
+ if (ArrayList_Count(server->clients) < 1)
+ return 1;
+
+ surfaceRect.left = surface->x;
+ surfaceRect.top = surface->y;
+ surfaceRect.right = surface->x + surface->width;
+ surfaceRect.bottom = surface->y + surface->height;
+ region16_intersect_rect(&(surface->invalidRegion), &(surface->invalidRegion), &surfaceRect);
+
+ if (region16_is_empty(&(surface->invalidRegion)))
+ return 1;
+
+ extents = region16_extents(&(surface->invalidRegion));
+ CopyMemory(&invalidRect, extents, sizeof(RECTANGLE_16));
+ shadow_capture_align_clip_rect(&invalidRect, &surfaceRect);
+ x = invalidRect.left;
+ y = invalidRect.top;
+ width = invalidRect.right - invalidRect.left;
+ height = invalidRect.bottom - invalidRect.top;
+
+ if (0)
+ {
+ x = 0;
+ y = 0;
+ width = surface->width;
+ height = surface->height;
+ }
+
+ WLog_INFO(TAG, "SurfaceCopy x: %d y: %d width: %d height: %d right: %d bottom: %d", x, y, width,
+ height, x + width, y + height);
+#if defined(WITH_WDS_API)
+ {
+ rdpGdi* gdi;
+ shwContext* shw;
+ rdpContext* context;
+
+ WINPR_ASSERT(subsystem);
+ shw = subsystem->shw;
+ WINPR_ASSERT(shw);
+
+ context = &shw->common.context;
+ WINPR_ASSERT(context);
+
+ gdi = context->gdi;
+ WINPR_ASSERT(gdi);
+
+ pDstData = gdi->primary_buffer;
+ nDstStep = gdi->width * 4;
+ DstFormat = gdi->dstFormat;
+ }
+#elif defined(WITH_DXGI_1_2)
+ DstFormat = PIXEL_FORMAT_BGRX32;
+ status = win_shadow_dxgi_fetch_frame_data(subsystem, &pDstData, &nDstStep, x, y, width, height);
+#endif
+
+ if (status <= 0)
+ return status;
+
+ if (!freerdp_image_copy(surface->data, surface->format, surface->scanline, x, y, width, height,
+ pDstData, DstFormat, nDstStep, x, y, NULL, FREERDP_FLIP_NONE))
+ return ERROR_INTERNAL_ERROR;
+
+ ArrayList_Lock(server->clients);
+ count = ArrayList_Count(server->clients);
+ shadow_subsystem_frame_update(&subsystem->base);
+ ArrayList_Unlock(server->clients);
+ region16_clear(&(surface->invalidRegion));
+ return 1;
+}
+
+#if defined(WITH_WDS_API)
+
+static DWORD WINAPI win_shadow_subsystem_thread(LPVOID arg)
+{
+ winShadowSubsystem* subsystem = (winShadowSubsystem*)arg;
+ DWORD status;
+ DWORD nCount;
+ HANDLE events[32];
+ HANDLE StopEvent;
+ StopEvent = subsystem->base.server->StopEvent;
+ nCount = 0;
+ events[nCount++] = StopEvent;
+ events[nCount++] = subsystem->RdpUpdateEnterEvent;
+
+ while (1)
+ {
+ status = WaitForMultipleObjects(nCount, events, FALSE, INFINITE);
+
+ if (WaitForSingleObject(StopEvent, 0) == WAIT_OBJECT_0)
+ {
+ break;
+ }
+
+ if (WaitForSingleObject(subsystem->RdpUpdateEnterEvent, 0) == WAIT_OBJECT_0)
+ {
+ win_shadow_surface_copy(subsystem);
+ ResetEvent(subsystem->RdpUpdateEnterEvent);
+ SetEvent(subsystem->RdpUpdateLeaveEvent);
+ }
+ }
+
+ ExitThread(0);
+ return 0;
+}
+
+#elif defined(WITH_DXGI_1_2)
+
+static DWORD WINAPI win_shadow_subsystem_thread(LPVOID arg)
+{
+ winShadowSubsystem* subsystem = (winShadowSubsystem*)arg;
+ int fps;
+ DWORD status;
+ DWORD nCount;
+ UINT64 cTime;
+ DWORD dwTimeout;
+ DWORD dwInterval;
+ UINT64 frameTime;
+ HANDLE events[32];
+ HANDLE StopEvent;
+ StopEvent = subsystem->server->StopEvent;
+ nCount = 0;
+ events[nCount++] = StopEvent;
+ fps = 16;
+ dwInterval = 1000 / fps;
+ frameTime = GetTickCount64() + dwInterval;
+
+ while (1)
+ {
+ dwTimeout = INFINITE;
+ cTime = GetTickCount64();
+ dwTimeout = (DWORD)((cTime > frameTime) ? 0 : frameTime - cTime);
+ status = WaitForMultipleObjects(nCount, events, FALSE, dwTimeout);
+
+ if (WaitForSingleObject(StopEvent, 0) == WAIT_OBJECT_0)
+ {
+ break;
+ }
+
+ if ((status == WAIT_TIMEOUT) || (GetTickCount64() > frameTime))
+ {
+ int dxgi_status;
+ dxgi_status = win_shadow_dxgi_get_next_frame(subsystem);
+
+ if (dxgi_status > 0)
+ dxgi_status = win_shadow_dxgi_get_invalid_region(subsystem);
+
+ if (dxgi_status > 0)
+ win_shadow_surface_copy(subsystem);
+
+ dwInterval = 1000 / fps;
+ frameTime += dwInterval;
+ }
+ }
+
+ ExitThread(0);
+ return 0;
+}
+
+#endif
+
+static UINT32 win_shadow_enum_monitors(MONITOR_DEF* monitors, UINT32 maxMonitors)
+{
+ HDC hdc;
+ int index;
+ int desktopWidth;
+ int desktopHeight;
+ DWORD iDevNum = 0;
+ int numMonitors = 0;
+ MONITOR_DEF* monitor;
+ DISPLAY_DEVICE displayDevice = { 0 };
+
+ displayDevice.cb = sizeof(DISPLAY_DEVICE);
+
+ if (EnumDisplayDevices(NULL, iDevNum, &displayDevice, 0))
+ {
+ hdc = CreateDC(displayDevice.DeviceName, NULL, NULL, NULL);
+ desktopWidth = GetDeviceCaps(hdc, HORZRES);
+ desktopHeight = GetDeviceCaps(hdc, VERTRES);
+ index = 0;
+ numMonitors = 1;
+ monitor = &monitors[index];
+ monitor->left = 0;
+ monitor->top = 0;
+ monitor->right = desktopWidth;
+ monitor->bottom = desktopHeight;
+ monitor->flags = 1;
+ DeleteDC(hdc);
+ }
+
+ return numMonitors;
+}
+
+static int win_shadow_subsystem_init(rdpShadowSubsystem* arg)
+{
+ winShadowSubsystem* subsystem = (winShadowSubsystem*)arg;
+ int status;
+ MONITOR_DEF* virtualScreen;
+ subsystem->base.numMonitors = win_shadow_enum_monitors(subsystem->base.monitors, 16);
+#if defined(WITH_WDS_API)
+ status = win_shadow_wds_init(subsystem);
+#elif defined(WITH_DXGI_1_2)
+ status = win_shadow_dxgi_init(subsystem);
+#endif
+ virtualScreen = &(subsystem->base.virtualScreen);
+ virtualScreen->left = 0;
+ virtualScreen->top = 0;
+ virtualScreen->right = subsystem->width;
+ virtualScreen->bottom = subsystem->height;
+ virtualScreen->flags = 1;
+ WLog_INFO(TAG, "width: %d height: %d", subsystem->width, subsystem->height);
+ return 1;
+}
+
+static int win_shadow_subsystem_uninit(rdpShadowSubsystem* arg)
+{
+ winShadowSubsystem* subsystem = (winShadowSubsystem*)arg;
+
+ if (!subsystem)
+ return -1;
+
+#if defined(WITH_WDS_API)
+ win_shadow_wds_uninit(subsystem);
+#elif defined(WITH_DXGI_1_2)
+ win_shadow_dxgi_uninit(subsystem);
+#endif
+ return 1;
+}
+
+static int win_shadow_subsystem_start(rdpShadowSubsystem* arg)
+{
+ winShadowSubsystem* subsystem = (winShadowSubsystem*)arg;
+ HANDLE thread;
+
+ if (!subsystem)
+ return -1;
+
+ if (!(thread = CreateThread(NULL, 0, win_shadow_subsystem_thread, (void*)subsystem, 0, NULL)))
+ {
+ WLog_ERR(TAG, "Failed to create thread");
+ return -1;
+ }
+
+ return 1;
+}
+
+static int win_shadow_subsystem_stop(rdpShadowSubsystem* arg)
+{
+ winShadowSubsystem* subsystem = (winShadowSubsystem*)arg;
+
+ if (!subsystem)
+ return -1;
+
+ return 1;
+}
+
+static void win_shadow_subsystem_free(rdpShadowSubsystem* arg)
+{
+ winShadowSubsystem* subsystem = (winShadowSubsystem*)arg;
+
+ if (!subsystem)
+ return;
+
+ win_shadow_subsystem_uninit(arg);
+ free(subsystem);
+}
+
+static rdpShadowSubsystem* win_shadow_subsystem_new(void)
+{
+ winShadowSubsystem* subsystem;
+ subsystem = (winShadowSubsystem*)calloc(1, sizeof(winShadowSubsystem));
+
+ if (!subsystem)
+ return NULL;
+
+ subsystem->base.SynchronizeEvent = win_shadow_input_synchronize_event;
+ subsystem->base.KeyboardEvent = win_shadow_input_keyboard_event;
+ subsystem->base.UnicodeKeyboardEvent = win_shadow_input_unicode_keyboard_event;
+ subsystem->base.MouseEvent = win_shadow_input_mouse_event;
+ subsystem->base.ExtendedMouseEvent = win_shadow_input_extended_mouse_event;
+ return &subsystem->base;
+}
+
+FREERDP_API const char* ShadowSubsystemName(void)
+{
+ return "Win";
+}
+
+FREERDP_API int ShadowSubsystemEntry(RDP_SHADOW_ENTRY_POINTS* pEntryPoints)
+{
+ const char name[] = "windows shadow subsystem";
+ const char* arg[] = { name };
+
+ freerdp_server_warn_unmaintained(ARRAYSIZE(arg), arg);
+ pEntryPoints->New = win_shadow_subsystem_new;
+ pEntryPoints->Free = win_shadow_subsystem_free;
+ pEntryPoints->Init = win_shadow_subsystem_init;
+ pEntryPoints->Uninit = win_shadow_subsystem_uninit;
+ pEntryPoints->Start = win_shadow_subsystem_start;
+ pEntryPoints->Stop = win_shadow_subsystem_stop;
+ pEntryPoints->EnumMonitors = win_shadow_enum_monitors;
+ return 1;
+}
diff --git a/server/shadow/Win/win_shadow.h b/server/shadow/Win/win_shadow.h
new file mode 100644
index 0000000..835bb66
--- /dev/null
+++ b/server/shadow/Win/win_shadow.h
@@ -0,0 +1,89 @@
+/**
+ * FreeRDP: A Remote Desktop Protocol Implementation
+ *
+ * Copyright 2011-2014 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.
+ */
+
+#ifndef FREERDP_SERVER_SHADOW_WIN_H
+#define FREERDP_SERVER_SHADOW_WIN_H
+
+#include <freerdp/assistance.h>
+
+#include <freerdp/server/shadow.h>
+
+typedef struct win_shadow_subsystem winShadowSubsystem;
+
+#include <winpr/crt.h>
+#include <winpr/synch.h>
+#include <winpr/thread.h>
+#include <winpr/stream.h>
+#include <winpr/collections.h>
+
+#include "win_rdp.h"
+#include "win_wds.h"
+#include "win_dxgi.h"
+
+struct win_shadow_subsystem
+{
+ rdpShadowSubsystem base;
+
+ int bpp;
+ int width;
+ int height;
+
+#ifdef WITH_WDS_API
+ HWND hWnd;
+ shwContext* shw;
+ HANDLE RdpUpdateEnterEvent;
+ HANDLE RdpUpdateLeaveEvent;
+ rdpAssistanceFile* pAssistanceFile;
+ _IRDPSessionEvents* pSessionEvents;
+ IRDPSRAPISharingSession* pSharingSession;
+ IRDPSRAPIInvitation* pInvitation;
+ IRDPSRAPIInvitationManager* pInvitationMgr;
+ IRDPSRAPISessionProperties* pSessionProperties;
+ IRDPSRAPIVirtualChannelManager* pVirtualChannelMgr;
+ IRDPSRAPIApplicationFilter* pApplicationFilter;
+ IRDPSRAPIAttendeeManager* pAttendeeMgr;
+#endif
+
+#ifdef WITH_DXGI_1_2
+ UINT pendingFrames;
+ BYTE* MetadataBuffer;
+ UINT MetadataBufferSize;
+ BOOL dxgiSurfaceMapped;
+ BOOL dxgiFrameAcquired;
+ ID3D11Device* dxgiDevice;
+ IDXGISurface* dxgiSurface;
+ ID3D11Texture2D* dxgiStage;
+ IDXGIResource* dxgiResource;
+ D3D_FEATURE_LEVEL featureLevel;
+ ID3D11Texture2D* dxgiDesktopImage;
+ DXGI_OUTDUPL_FRAME_INFO dxgiFrameInfo;
+ ID3D11DeviceContext* dxgiDeviceContext;
+ IDXGIOutputDuplication* dxgiOutputDuplication;
+#endif
+};
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* FREERDP_SERVER_SHADOW_WIN_H */
diff --git a/server/shadow/Win/win_wds.c b/server/shadow/Win/win_wds.c
new file mode 100644
index 0000000..197e61f
--- /dev/null
+++ b/server/shadow/Win/win_wds.c
@@ -0,0 +1,850 @@
+/**
+ * FreeRDP: A Remote Desktop Protocol Implementation
+ *
+ * Copyright 2014 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.
+ */
+
+#include <freerdp/config.h>
+
+#include <winpr/crt.h>
+#include <winpr/print.h>
+#include <freerdp/log.h>
+
+#include "win_rdp.h"
+
+#include "win_wds.h"
+
+/**
+ * Windows Desktop Sharing API:
+ * http://blogs.msdn.com/b/rds/archive/2007/03/08/windows-desktop-sharing-api.aspx
+ *
+ * Windows Desktop Sharing Interfaces:
+ * http://msdn.microsoft.com/en-us/library/aa373871%28v=vs.85%29.aspx
+ *
+ * Offer Remote Assistance Sample C:
+ * http://msdn.microsoft.com/en-us/library/ms811079.aspx#remoteassistanceapi_topic2b
+ *
+ * Remote Assistance in XP: Programmatically establish an RDP session:
+ * http://www.codeproject.com/Articles/29939/Remote-Assistance-in-XP-Programmatically-establish
+ */
+
+#undef DEFINE_GUID
+#define INITGUID
+
+#include <initguid.h>
+
+#include <freerdp/assistance.h>
+
+#define TAG SERVER_TAG("shadow.win")
+
+DEFINE_GUID(CLSID_RDPSession, 0x9B78F0E6, 0x3E05, 0x4A5B, 0xB2, 0xE8, 0xE7, 0x43, 0xA8, 0x95, 0x6B,
+ 0x65);
+DEFINE_GUID(DIID__IRDPSessionEvents, 0x98a97042, 0x6698, 0x40e9, 0x8e, 0xfd, 0xb3, 0x20, 0x09, 0x90,
+ 0x00, 0x4b);
+DEFINE_GUID(IID_IRDPSRAPISharingSession, 0xeeb20886, 0xe470, 0x4cf6, 0x84, 0x2b, 0x27, 0x39, 0xc0,
+ 0xec, 0x5c, 0xfb);
+DEFINE_GUID(IID_IRDPSRAPIAttendee, 0xec0671b3, 0x1b78, 0x4b80, 0xa4, 0x64, 0x91, 0x32, 0x24, 0x75,
+ 0x43, 0xe3);
+DEFINE_GUID(IID_IRDPSRAPIAttendeeManager, 0xba3a37e8, 0x33da, 0x4749, 0x8d, 0xa0, 0x07, 0xfa, 0x34,
+ 0xda, 0x79, 0x44);
+DEFINE_GUID(IID_IRDPSRAPISessionProperties, 0x339b24f2, 0x9bc0, 0x4f16, 0x9a, 0xac, 0xf1, 0x65,
+ 0x43, 0x3d, 0x13, 0xd4);
+DEFINE_GUID(CLSID_RDPSRAPIApplicationFilter, 0xe35ace89, 0xc7e8, 0x427e, 0xa4, 0xf9, 0xb9, 0xda,
+ 0x07, 0x28, 0x26, 0xbd);
+DEFINE_GUID(CLSID_RDPSRAPIInvitationManager, 0x53d9c9db, 0x75ab, 0x4271, 0x94, 0x8a, 0x4c, 0x4e,
+ 0xb3, 0x6a, 0x8f, 0x2b);
+
+static ULONG Shadow_IRDPSessionEvents_RefCount = 0;
+
+const char* GetRDPSessionEventString(DISPID id)
+{
+ switch (id)
+ {
+ case DISPID_RDPSRAPI_EVENT_ON_ATTENDEE_CONNECTED:
+ return "OnAttendeeConnected";
+ break;
+
+ case DISPID_RDPSRAPI_EVENT_ON_ATTENDEE_DISCONNECTED:
+ return "OnAttendeeDisconnected";
+ break;
+
+ case DISPID_RDPSRAPI_EVENT_ON_ATTENDEE_UPDATE:
+ return "OnAttendeeUpdate";
+ break;
+
+ case DISPID_RDPSRAPI_EVENT_ON_ERROR:
+ return "OnError";
+ break;
+
+ case DISPID_RDPSRAPI_EVENT_ON_VIEWER_CONNECTED:
+ return "OnConnectionEstablished";
+ break;
+
+ case DISPID_RDPSRAPI_EVENT_ON_VIEWER_DISCONNECTED:
+ return "OnConnectionTerminated";
+ break;
+
+ case DISPID_RDPSRAPI_EVENT_ON_VIEWER_AUTHENTICATED:
+ return "OnConnectionAuthenticated";
+ break;
+
+ case DISPID_RDPSRAPI_EVENT_ON_VIEWER_CONNECTFAILED:
+ return "OnConnectionFailed";
+ break;
+
+ case DISPID_RDPSRAPI_EVENT_ON_CTRLLEVEL_CHANGE_REQUEST:
+ return "OnControlLevelChangeRequest";
+ break;
+
+ case DISPID_RDPSRAPI_EVENT_ON_GRAPHICS_STREAM_PAUSED:
+ return "OnGraphicsStreamPaused";
+ break;
+
+ case DISPID_RDPSRAPI_EVENT_ON_GRAPHICS_STREAM_RESUMED:
+ return "OnGraphicsStreamResumed";
+ break;
+
+ case DISPID_RDPSRAPI_EVENT_ON_VIRTUAL_CHANNEL_JOIN:
+ return "OnChannelJoin";
+ break;
+
+ case DISPID_RDPSRAPI_EVENT_ON_VIRTUAL_CHANNEL_LEAVE:
+ return "OnChannelLeave";
+ break;
+
+ case DISPID_RDPSRAPI_EVENT_ON_VIRTUAL_CHANNEL_DATARECEIVED:
+ return "OnChannelDataReceived";
+ break;
+
+ case DISPID_RDPSRAPI_EVENT_ON_VIRTUAL_CHANNEL_SENDCOMPLETED:
+ return "OnChannelDataSent";
+ break;
+
+ case DISPID_RDPSRAPI_EVENT_ON_APPLICATION_OPEN:
+ return "OnApplicationOpen";
+ break;
+
+ case DISPID_RDPSRAPI_EVENT_ON_APPLICATION_CLOSE:
+ return "OnApplicationClose";
+ break;
+
+ case DISPID_RDPSRAPI_EVENT_ON_APPLICATION_UPDATE:
+ return "OnApplicationUpdate";
+ break;
+
+ case DISPID_RDPSRAPI_EVENT_ON_WINDOW_OPEN:
+ return "OnWindowOpen";
+ break;
+
+ case DISPID_RDPSRAPI_EVENT_ON_WINDOW_CLOSE:
+ return "OnWindowClose";
+ break;
+
+ case DISPID_RDPSRAPI_EVENT_ON_WINDOW_UPDATE:
+ return "OnWindowUpdate";
+ break;
+
+ case DISPID_RDPSRAPI_EVENT_ON_APPFILTER_UPDATE:
+ return "OnAppFilterUpdate";
+ break;
+
+ case DISPID_RDPSRAPI_EVENT_ON_SHARED_RECT_CHANGED:
+ return "OnSharedRectChanged";
+ break;
+
+ case DISPID_RDPSRAPI_EVENT_ON_FOCUSRELEASED:
+ return "OnFocusReleased";
+ break;
+
+ case DISPID_RDPSRAPI_EVENT_ON_SHARED_DESKTOP_SETTINGS_CHANGED:
+ return "OnSharedDesktopSettingsChanged";
+ break;
+
+ case DISPID_RDPAPI_EVENT_ON_BOUNDING_RECT_CHANGED:
+ return "OnViewingSizeChanged";
+ break;
+ }
+
+ return "OnUnknown";
+}
+
+static HRESULT STDMETHODCALLTYPE
+Shadow_IRDPSessionEvents_QueryInterface(__RPC__in _IRDPSessionEvents* This,
+ /* [in] */ __RPC__in REFIID riid,
+ /* [annotation][iid_is][out] */
+ _COM_Outptr_ void** ppvObject)
+{
+ *ppvObject = NULL;
+
+ if (IsEqualIID(riid, &DIID__IRDPSessionEvents) || IsEqualIID(riid, &IID_IDispatch) ||
+ IsEqualIID(riid, &IID_IUnknown))
+ {
+ *ppvObject = This;
+ }
+
+ if (!(*ppvObject))
+ return E_NOINTERFACE;
+
+ This->lpVtbl->AddRef(This);
+ return S_OK;
+}
+
+static ULONG STDMETHODCALLTYPE Shadow_IRDPSessionEvents_AddRef(__RPC__in _IRDPSessionEvents* This)
+{
+ Shadow_IRDPSessionEvents_RefCount++;
+ return Shadow_IRDPSessionEvents_RefCount;
+}
+
+static ULONG STDMETHODCALLTYPE Shadow_IRDPSessionEvents_Release(__RPC__in _IRDPSessionEvents* This)
+{
+ if (!Shadow_IRDPSessionEvents_RefCount)
+ return 0;
+
+ Shadow_IRDPSessionEvents_RefCount--;
+ return Shadow_IRDPSessionEvents_RefCount;
+}
+
+static HRESULT STDMETHODCALLTYPE
+Shadow_IRDPSessionEvents_GetTypeInfoCount(__RPC__in _IRDPSessionEvents* This,
+ /* [out] */ __RPC__out UINT* pctinfo)
+{
+ WLog_INFO(TAG, "Shadow_IRDPSessionEvents_GetTypeInfoCount");
+ *pctinfo = 1;
+ return S_OK;
+}
+
+static HRESULT STDMETHODCALLTYPE
+Shadow_IRDPSessionEvents_GetTypeInfo(__RPC__in _IRDPSessionEvents* This,
+ /* [in] */ UINT iTInfo,
+ /* [in] */ LCID lcid,
+ /* [out] */ __RPC__deref_out_opt ITypeInfo** ppTInfo)
+{
+ WLog_INFO(TAG, "Shadow_IRDPSessionEvents_GetTypeInfo");
+ return E_NOTIMPL;
+}
+
+static HRESULT STDMETHODCALLTYPE Shadow_IRDPSessionEvents_GetIDsOfNames(
+ __RPC__in _IRDPSessionEvents* This,
+ /* [in] */ __RPC__in REFIID riid,
+ /* [size_is][in] */ __RPC__in_ecount_full(cNames) LPOLESTR* rgszNames,
+ /* [range][in] */ __RPC__in_range(0, 16384) UINT cNames,
+ /* [in] */ LCID lcid,
+ /* [size_is][out] */ __RPC__out_ecount_full(cNames) DISPID* rgDispId)
+{
+ WLog_INFO(TAG, "Shadow_IRDPSessionEvents_GetIDsOfNames");
+ return E_NOTIMPL;
+}
+
+static HRESULT STDMETHODCALLTYPE Shadow_IRDPSessionEvents_Invoke(_IRDPSessionEvents* This,
+ /* [annotation][in] */
+ _In_ DISPID dispIdMember,
+ /* [annotation][in] */
+ _In_ REFIID riid,
+ /* [annotation][in] */
+ _In_ LCID lcid,
+ /* [annotation][in] */
+ _In_ WORD wFlags,
+ /* [annotation][out][in] */
+ _In_ DISPPARAMS* pDispParams,
+ /* [annotation][out] */
+ _Out_opt_ VARIANT* pVarResult,
+ /* [annotation][out] */
+ _Out_opt_ EXCEPINFO* pExcepInfo,
+ /* [annotation][out] */
+ _Out_opt_ UINT* puArgErr)
+{
+ HRESULT hr;
+ VARIANT vr;
+ UINT uArgErr;
+ WLog_INFO(TAG, "%s (%ld)", GetRDPSessionEventString(dispIdMember), dispIdMember);
+
+ switch (dispIdMember)
+ {
+ case DISPID_RDPSRAPI_EVENT_ON_ATTENDEE_CONNECTED:
+ {
+ int level;
+ IDispatch* pDispatch;
+ IRDPSRAPIAttendee* pAttendee;
+ vr.vt = VT_DISPATCH;
+ vr.pdispVal = NULL;
+ hr = DispGetParam(pDispParams, 0, VT_DISPATCH, &vr, &uArgErr);
+
+ if (FAILED(hr))
+ {
+ WLog_ERR(TAG, "%s DispGetParam(0, VT_DISPATCH) failure: 0x%08lX",
+ GetRDPSessionEventString(dispIdMember), hr);
+ return hr;
+ }
+
+ pDispatch = vr.pdispVal;
+ hr = pDispatch->lpVtbl->QueryInterface(pDispatch, &IID_IRDPSRAPIAttendee,
+ (void**)&pAttendee);
+
+ if (FAILED(hr))
+ {
+ WLog_INFO(TAG, "%s IDispatch::QueryInterface(IRDPSRAPIAttendee) failure: 0x%08lX",
+ GetRDPSessionEventString(dispIdMember), hr);
+ return hr;
+ }
+
+ level = CTRL_LEVEL_VIEW;
+ // level = CTRL_LEVEL_INTERACTIVE;
+ hr = pAttendee->lpVtbl->put_ControlLevel(pAttendee, level);
+
+ if (FAILED(hr))
+ {
+ WLog_INFO(TAG, "%s IRDPSRAPIAttendee::put_ControlLevel() failure: 0x%08lX",
+ GetRDPSessionEventString(dispIdMember), hr);
+ return hr;
+ }
+
+ pAttendee->lpVtbl->Release(pAttendee);
+ }
+ break;
+
+ case DISPID_RDPSRAPI_EVENT_ON_ATTENDEE_DISCONNECTED:
+ break;
+
+ case DISPID_RDPSRAPI_EVENT_ON_ATTENDEE_UPDATE:
+ break;
+
+ case DISPID_RDPSRAPI_EVENT_ON_ERROR:
+ break;
+
+ case DISPID_RDPSRAPI_EVENT_ON_VIEWER_CONNECTED:
+ break;
+
+ case DISPID_RDPSRAPI_EVENT_ON_VIEWER_DISCONNECTED:
+ break;
+
+ case DISPID_RDPSRAPI_EVENT_ON_VIEWER_AUTHENTICATED:
+ break;
+
+ case DISPID_RDPSRAPI_EVENT_ON_VIEWER_CONNECTFAILED:
+ break;
+
+ case DISPID_RDPSRAPI_EVENT_ON_CTRLLEVEL_CHANGE_REQUEST:
+ {
+ int level;
+ IDispatch* pDispatch;
+ IRDPSRAPIAttendee* pAttendee;
+ vr.vt = VT_INT;
+ vr.pdispVal = NULL;
+ hr = DispGetParam(pDispParams, 1, VT_INT, &vr, &uArgErr);
+
+ if (FAILED(hr))
+ {
+ WLog_INFO(TAG, "%s DispGetParam(1, VT_INT) failure: 0x%08lX",
+ GetRDPSessionEventString(dispIdMember), hr);
+ return hr;
+ }
+
+ level = vr.intVal;
+ vr.vt = VT_DISPATCH;
+ vr.pdispVal = NULL;
+ hr = DispGetParam(pDispParams, 0, VT_DISPATCH, &vr, &uArgErr);
+
+ if (FAILED(hr))
+ {
+ WLog_ERR(TAG, "%s DispGetParam(0, VT_DISPATCH) failure: 0x%08lX",
+ GetRDPSessionEventString(dispIdMember), hr);
+ return hr;
+ }
+
+ pDispatch = vr.pdispVal;
+ hr = pDispatch->lpVtbl->QueryInterface(pDispatch, &IID_IRDPSRAPIAttendee,
+ (void**)&pAttendee);
+
+ if (FAILED(hr))
+ {
+ WLog_INFO(TAG, "%s IDispatch::QueryInterface(IRDPSRAPIAttendee) failure: 0x%08lX",
+ GetRDPSessionEventString(dispIdMember), hr);
+ return hr;
+ }
+
+ hr = pAttendee->lpVtbl->put_ControlLevel(pAttendee, level);
+
+ if (FAILED(hr))
+ {
+ WLog_INFO(TAG, "%s IRDPSRAPIAttendee::put_ControlLevel() failure: 0x%08lX",
+ GetRDPSessionEventString(dispIdMember), hr);
+ return hr;
+ }
+
+ pAttendee->lpVtbl->Release(pAttendee);
+ }
+ break;
+
+ case DISPID_RDPSRAPI_EVENT_ON_GRAPHICS_STREAM_PAUSED:
+ break;
+
+ case DISPID_RDPSRAPI_EVENT_ON_GRAPHICS_STREAM_RESUMED:
+ break;
+
+ case DISPID_RDPSRAPI_EVENT_ON_VIRTUAL_CHANNEL_JOIN:
+ break;
+
+ case DISPID_RDPSRAPI_EVENT_ON_VIRTUAL_CHANNEL_LEAVE:
+ break;
+
+ case DISPID_RDPSRAPI_EVENT_ON_VIRTUAL_CHANNEL_DATARECEIVED:
+ break;
+
+ case DISPID_RDPSRAPI_EVENT_ON_VIRTUAL_CHANNEL_SENDCOMPLETED:
+ break;
+
+ case DISPID_RDPSRAPI_EVENT_ON_APPLICATION_OPEN:
+ break;
+
+ case DISPID_RDPSRAPI_EVENT_ON_APPLICATION_CLOSE:
+ break;
+
+ case DISPID_RDPSRAPI_EVENT_ON_APPLICATION_UPDATE:
+ break;
+
+ case DISPID_RDPSRAPI_EVENT_ON_WINDOW_OPEN:
+ break;
+
+ case DISPID_RDPSRAPI_EVENT_ON_WINDOW_CLOSE:
+ break;
+
+ case DISPID_RDPSRAPI_EVENT_ON_WINDOW_UPDATE:
+ break;
+
+ case DISPID_RDPSRAPI_EVENT_ON_APPFILTER_UPDATE:
+ break;
+
+ case DISPID_RDPSRAPI_EVENT_ON_SHARED_RECT_CHANGED:
+ break;
+
+ case DISPID_RDPSRAPI_EVENT_ON_FOCUSRELEASED:
+ break;
+
+ case DISPID_RDPSRAPI_EVENT_ON_SHARED_DESKTOP_SETTINGS_CHANGED:
+ break;
+
+ case DISPID_RDPAPI_EVENT_ON_BOUNDING_RECT_CHANGED:
+ break;
+ }
+
+ return S_OK;
+}
+
+static _IRDPSessionEventsVtbl Shadow_IRDPSessionEventsVtbl = {
+ /* IUnknown */
+ Shadow_IRDPSessionEvents_QueryInterface, Shadow_IRDPSessionEvents_AddRef,
+ Shadow_IRDPSessionEvents_Release,
+
+ /* IDispatch */
+ Shadow_IRDPSessionEvents_GetTypeInfoCount, Shadow_IRDPSessionEvents_GetTypeInfo,
+ Shadow_IRDPSessionEvents_GetIDsOfNames, Shadow_IRDPSessionEvents_Invoke
+};
+
+static _IRDPSessionEvents Shadow_IRDPSessionEvents = { &Shadow_IRDPSessionEventsVtbl };
+
+static LRESULT CALLBACK ShadowWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ switch (uMsg)
+ {
+ case WM_CLOSE:
+ DestroyWindow(hwnd);
+ break;
+
+ case WM_DESTROY:
+ PostQuitMessage(0);
+ break;
+
+ default:
+ return DefWindowProc(hwnd, uMsg, wParam, lParam);
+ break;
+ }
+
+ return 0;
+}
+
+int win_shadow_wds_wnd_init(winShadowSubsystem* subsystem)
+{
+ HMODULE hModule;
+ HINSTANCE hInstance;
+ WNDCLASSEX wndClassEx = { 0 };
+ hModule = GetModuleHandle(NULL);
+
+ wndClassEx.cbSize = sizeof(WNDCLASSEX);
+ wndClassEx.style = 0;
+ wndClassEx.lpfnWndProc = ShadowWndProc;
+ wndClassEx.cbClsExtra = 0;
+ wndClassEx.cbWndExtra = 0;
+ wndClassEx.hInstance = hModule;
+ wndClassEx.hIcon = NULL;
+ wndClassEx.hCursor = NULL;
+ wndClassEx.hbrBackground = NULL;
+ wndClassEx.lpszMenuName = _T("ShadowWndMenu");
+ wndClassEx.lpszClassName = _T("ShadowWndClass");
+ wndClassEx.hIconSm = NULL;
+
+ if (!RegisterClassEx(&wndClassEx))
+ {
+ WLog_ERR(TAG, "RegisterClassEx failure");
+ return -1;
+ }
+
+ hInstance = wndClassEx.hInstance;
+ subsystem->hWnd = CreateWindowEx(0, wndClassEx.lpszClassName, 0, 0, 0, 0, 0, 0, HWND_MESSAGE, 0,
+ hInstance, NULL);
+
+ if (!subsystem->hWnd)
+ {
+ WLog_INFO(TAG, "CreateWindowEx failure");
+ return -1;
+ }
+
+ return 1;
+}
+
+int win_shadow_wds_init(winShadowSubsystem* subsystem)
+{
+ int status;
+ HRESULT hr;
+ DWORD dwCookie;
+ long left, top;
+ long right, bottom;
+ long width, height;
+ IUnknown* pUnknown;
+ rdpSettings* settings;
+ BSTR bstrAuthString;
+ BSTR bstrGroupName;
+ BSTR bstrPassword;
+ BSTR bstrPropertyName;
+ VARIANT varPropertyValue;
+ rdpAssistanceFile* file;
+ IConnectionPoint* pCP;
+ IConnectionPointContainer* pCPC;
+ win_shadow_wds_wnd_init(subsystem);
+ hr = OleInitialize(NULL);
+
+ if (FAILED(hr))
+ {
+ WLog_ERR(TAG, "OleInitialize() failure");
+ return -1;
+ }
+
+ hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
+
+ if (FAILED(hr))
+ {
+ WLog_ERR(TAG, "CoInitialize() failure");
+ return -1;
+ }
+
+ hr = CoCreateInstance(&CLSID_RDPSession, NULL, CLSCTX_ALL, &IID_IRDPSRAPISharingSession,
+ (void**)&(subsystem->pSharingSession));
+
+ if (FAILED(hr))
+ {
+ WLog_ERR(TAG, "CoCreateInstance(IRDPSRAPISharingSession) failure: 0x%08lX", hr);
+ return -1;
+ }
+
+ pUnknown = (IUnknown*)subsystem->pSharingSession;
+ hr = pUnknown->lpVtbl->QueryInterface(pUnknown, &IID_IConnectionPointContainer, (void**)&pCPC);
+
+ if (FAILED(hr))
+ {
+ WLog_ERR(TAG, "QueryInterface(IID_IConnectionPointContainer) failure: 0x%08lX", hr);
+ return -1;
+ }
+
+ pCPC->lpVtbl->FindConnectionPoint(pCPC, &DIID__IRDPSessionEvents, &pCP);
+
+ if (FAILED(hr))
+ {
+ WLog_ERR(
+ TAG,
+ "IConnectionPointContainer::FindConnectionPoint(_IRDPSessionEvents) failure: 0x%08lX",
+ hr);
+ return -1;
+ }
+
+ dwCookie = 0;
+ subsystem->pSessionEvents = &Shadow_IRDPSessionEvents;
+ subsystem->pSessionEvents->lpVtbl->AddRef(subsystem->pSessionEvents);
+ hr = pCP->lpVtbl->Advise(pCP, (IUnknown*)subsystem->pSessionEvents, &dwCookie);
+
+ if (FAILED(hr))
+ {
+ WLog_ERR(TAG, "IConnectionPoint::Advise(Shadow_IRDPSessionEvents) failure: 0x%08lX", hr);
+ return -1;
+ }
+
+ hr = subsystem->pSharingSession->lpVtbl->put_ColorDepth(subsystem->pSharingSession, 32);
+
+ if (FAILED(hr))
+ {
+ WLog_ERR(TAG, "IRDPSRAPISharingSession::put_ColorDepth() failure: 0x%08lX", hr);
+ return -1;
+ }
+
+ hr = subsystem->pSharingSession->lpVtbl->GetDesktopSharedRect(subsystem->pSharingSession, &left,
+ &top, &right, &bottom);
+
+ if (FAILED(hr))
+ {
+ WLog_ERR(TAG, "IRDPSRAPISharingSession::GetDesktopSharedRect() failure: 0x%08lX", hr);
+ return -1;
+ }
+
+ width = right - left;
+ height = bottom - top;
+ WLog_INFO(
+ TAG,
+ "GetDesktopSharedRect(): left: %ld top: %ld right: %ld bottom: %ld width: %ld height: %ld",
+ left, top, right, bottom, width, height);
+ hr = subsystem->pSharingSession->lpVtbl->get_VirtualChannelManager(
+ subsystem->pSharingSession, &(subsystem->pVirtualChannelMgr));
+
+ if (FAILED(hr))
+ {
+ WLog_ERR(TAG, "IRDPSRAPISharingSession::get_VirtualChannelManager() failure: 0x%08lX", hr);
+ return -1;
+ }
+
+ hr = subsystem->pSharingSession->lpVtbl->get_ApplicationFilter(
+ subsystem->pSharingSession, &(subsystem->pApplicationFilter));
+
+ if (FAILED(hr))
+ {
+ WLog_ERR(TAG, "IRDPSRAPISharingSession::get_ApplicationFilter() failure: 0x%08lX", hr);
+ return -1;
+ }
+
+ hr = subsystem->pSharingSession->lpVtbl->get_Attendees(subsystem->pSharingSession,
+ &(subsystem->pAttendeeMgr));
+
+ if (FAILED(hr))
+ {
+ WLog_ERR(TAG, "IRDPSRAPISharingSession::get_Attendees() failure: 0x%08lX", hr);
+ return -1;
+ }
+
+ hr = subsystem->pSharingSession->lpVtbl->get_Properties(subsystem->pSharingSession,
+ &(subsystem->pSessionProperties));
+
+ if (FAILED(hr))
+ {
+ WLog_ERR(TAG, "IRDPSRAPISharingSession::get_Properties() failure: 0x%08lX", hr);
+ return -1;
+ }
+
+ bstrPropertyName = SysAllocString(L"PortId");
+ varPropertyValue.vt = VT_I4;
+ varPropertyValue.intVal = 40000;
+ hr = subsystem->pSessionProperties->lpVtbl->put_Property(subsystem->pSessionProperties,
+ bstrPropertyName, varPropertyValue);
+ SysFreeString(bstrPropertyName);
+
+ if (FAILED(hr))
+ {
+ WLog_ERR(TAG, "IRDPSRAPISessionProperties::put_Property(PortId) failure: 0x%08lX", hr);
+ return -1;
+ }
+
+ bstrPropertyName = SysAllocString(L"DrvConAttach");
+ varPropertyValue.vt = VT_BOOL;
+ varPropertyValue.boolVal = VARIANT_TRUE;
+ hr = subsystem->pSessionProperties->lpVtbl->put_Property(subsystem->pSessionProperties,
+ bstrPropertyName, varPropertyValue);
+ SysFreeString(bstrPropertyName);
+
+ if (FAILED(hr))
+ {
+ WLog_ERR(TAG, "IRDPSRAPISessionProperties::put_Property(DrvConAttach) failure: 0x%08lX",
+ hr);
+ return -1;
+ }
+
+ bstrPropertyName = SysAllocString(L"PortProtocol");
+ varPropertyValue.vt = VT_I4;
+ // varPropertyValue.intVal = 0; // AF_UNSPEC
+ varPropertyValue.intVal = 2; // AF_INET
+ // varPropertyValue.intVal = 23; // AF_INET6
+ hr = subsystem->pSessionProperties->lpVtbl->put_Property(subsystem->pSessionProperties,
+ bstrPropertyName, varPropertyValue);
+ SysFreeString(bstrPropertyName);
+
+ if (FAILED(hr))
+ {
+ WLog_ERR(TAG, "IRDPSRAPISessionProperties::put_Property(PortProtocol) failure: 0x%08lX",
+ hr);
+ return -1;
+ }
+
+ hr = subsystem->pSharingSession->lpVtbl->Open(subsystem->pSharingSession);
+
+ if (FAILED(hr))
+ {
+ WLog_ERR(TAG, "IRDPSRAPISharingSession::Open() failure: 0x%08lX", hr);
+ return -1;
+ }
+
+ hr = subsystem->pSharingSession->lpVtbl->get_Invitations(subsystem->pSharingSession,
+ &(subsystem->pInvitationMgr));
+
+ if (FAILED(hr))
+ {
+ WLog_ERR(TAG, "IRDPSRAPISharingSession::get_Invitations() failure");
+ return -1;
+ }
+
+ bstrAuthString = SysAllocString(L"Shadow");
+ bstrGroupName = SysAllocString(L"ShadowGroup");
+ bstrPassword = SysAllocString(L"Shadow123!");
+ hr = subsystem->pInvitationMgr->lpVtbl->CreateInvitation(
+ subsystem->pInvitationMgr, bstrAuthString, bstrGroupName, bstrPassword, 5,
+ &(subsystem->pInvitation));
+ SysFreeString(bstrAuthString);
+ SysFreeString(bstrGroupName);
+ SysFreeString(bstrPassword);
+
+ if (FAILED(hr))
+ {
+ WLog_ERR(TAG, "IRDPSRAPIInvitationManager::CreateInvitation() failure: 0x%08lX", hr);
+ return -1;
+ }
+
+ file = subsystem->pAssistanceFile = freerdp_assistance_file_new();
+
+ if (!file)
+ {
+ WLog_ERR(TAG, "freerdp_assistance_file_new() failed");
+ return -1;
+ }
+
+ {
+ int status2 = -1;
+ char* ConnectionString2;
+ BSTR bstrConnectionString;
+ hr = subsystem->pInvitation->lpVtbl->get_ConnectionString(subsystem->pInvitation,
+ &bstrConnectionString);
+
+ if (FAILED(hr))
+ {
+ WLog_ERR(TAG, "IRDPSRAPIInvitation::get_ConnectionString() failure: 0x%08lX", hr);
+ return -1;
+ }
+
+ ConnectionString2 = ConvertWCharToUtf8Alloc(bstrConnectionString, NULL);
+ SysFreeString(bstrConnectionString);
+ status2 = freerdp_assistance_set_connection_string2(file, ConnectionString2, "Shadow123!");
+ free(ConnectionString2);
+
+ if ((!ConnectionString2) || (status2 < 1))
+ {
+ WLog_ERR(TAG, "failed to convert connection string");
+ return -1;
+ }
+ }
+
+ freerdp_assistance_print_file(file, WLog_Get(TAG), WLOG_INFO);
+ status = win_shadow_rdp_init(subsystem);
+
+ if (status < 0)
+ {
+ WLog_ERR(TAG, "win_shadow_rdp_init() failure: %d", status);
+ return status;
+ }
+
+ settings = subsystem->shw->settings;
+ status = freerdp_assistance_populate_settings_from_assistance_file(file, settings);
+ freerdp_settings_set_string(settings, FreeRDP_Domain, "RDP");
+ freerdp_settings_set_string(settings, FreeRDP_Username, "Shadow");
+ freerdp_settings_set_bool(settings, FreeRDP_AutoLogonEnabled, TRUE);
+ freerdp_settings_set_uint32(settings, FreeRDP_DesktopWidth, width);
+ freerdp_settings_set_uint32(settings, FreeRDP_DesktopHeight, height);
+ status = win_shadow_rdp_start(subsystem);
+
+ if (status < 0)
+ {
+ WLog_ERR(TAG, "win_shadow_rdp_start() failure: %d", status);
+ return status;
+ }
+
+ return 1;
+}
+
+int win_shadow_wds_uninit(winShadowSubsystem* subsystem)
+{
+ if (subsystem->pSharingSession)
+ {
+ subsystem->pSharingSession->lpVtbl->Close(subsystem->pSharingSession);
+ subsystem->pSharingSession->lpVtbl->Release(subsystem->pSharingSession);
+ subsystem->pSharingSession = NULL;
+ }
+
+ if (subsystem->pVirtualChannelMgr)
+ {
+ subsystem->pVirtualChannelMgr->lpVtbl->Release(subsystem->pVirtualChannelMgr);
+ subsystem->pVirtualChannelMgr = NULL;
+ }
+
+ if (subsystem->pApplicationFilter)
+ {
+ subsystem->pApplicationFilter->lpVtbl->Release(subsystem->pApplicationFilter);
+ subsystem->pApplicationFilter = NULL;
+ }
+
+ if (subsystem->pAttendeeMgr)
+ {
+ subsystem->pAttendeeMgr->lpVtbl->Release(subsystem->pAttendeeMgr);
+ subsystem->pAttendeeMgr = NULL;
+ }
+
+ if (subsystem->pSessionProperties)
+ {
+ subsystem->pSessionProperties->lpVtbl->Release(subsystem->pSessionProperties);
+ subsystem->pSessionProperties = NULL;
+ }
+
+ if (subsystem->pInvitationMgr)
+ {
+ subsystem->pInvitationMgr->lpVtbl->Release(subsystem->pInvitationMgr);
+ subsystem->pInvitationMgr = NULL;
+ }
+
+ if (subsystem->pInvitation)
+ {
+ subsystem->pInvitation->lpVtbl->Release(subsystem->pInvitation);
+ subsystem->pInvitation = NULL;
+ }
+
+ if (subsystem->pAssistanceFile)
+ {
+ freerdp_assistance_file_free(subsystem->pAssistanceFile);
+ subsystem->pAssistanceFile = NULL;
+ }
+
+ if (subsystem->hWnd)
+ {
+ DestroyWindow(subsystem->hWnd);
+ subsystem->hWnd = NULL;
+ }
+
+ if (subsystem->shw)
+ {
+ win_shadow_rdp_uninit(subsystem);
+ subsystem->shw = NULL;
+ }
+
+ return 1;
+}
diff --git a/server/shadow/Win/win_wds.h b/server/shadow/Win/win_wds.h
new file mode 100644
index 0000000..f8f3d80
--- /dev/null
+++ b/server/shadow/Win/win_wds.h
@@ -0,0 +1,48 @@
+/**
+ * FreeRDP: A Remote Desktop Protocol Implementation
+ *
+ * Copyright 2014 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.
+ */
+
+#ifndef FREERDP_SERVER_SHADOW_WIN_WDS_H
+#define FREERDP_SERVER_SHADOW_WIN_WDS_H
+
+#define WITH_WDS_API 1
+
+#ifndef CINTERFACE
+#define CINTERFACE
+#endif
+
+#include <rdpencomapi.h>
+
+#ifndef DISPID_RDPAPI_EVENT_ON_BOUNDING_RECT_CHANGED
+#define DISPID_RDPAPI_EVENT_ON_BOUNDING_RECT_CHANGED 340
+#endif
+
+#include "win_shadow.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+ int win_shadow_wds_init(winShadowSubsystem* subsystem);
+ int win_shadow_wds_uninit(winShadowSubsystem* subsystem);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* FREERDP_SERVER_SHADOW_WIN_WDS_H */
diff --git a/server/shadow/X11/CMakeLists.txt b/server/shadow/X11/CMakeLists.txt
new file mode 100644
index 0000000..0d88a0e
--- /dev/null
+++ b/server/shadow/X11/CMakeLists.txt
@@ -0,0 +1,72 @@
+find_package(X11 REQUIRED)
+if(X11_FOUND)
+ add_definitions(-DWITH_X11)
+ include_directories(${X11_INCLUDE_DIR})
+ list(APPEND LIBS ${X11_LIBRARIES})
+endif()
+
+if(X11_XShm_FOUND)
+ add_definitions(-DWITH_XSHM)
+ include_directories(${X11_XShm_INCLUDE_PATH})
+ list(APPEND LIBS ${X11_Xext_LIB})
+endif()
+
+if(X11_Xext_FOUND)
+ add_definitions(-DWITH_XEXT)
+ list(APPEND LIBS ${X11_Xext_LIB})
+endif()
+
+if(X11_Xinerama_FOUND)
+ add_definitions(-DWITH_XINERAMA)
+ include_directories(${X11_Xinerama_INCLUDE_PATH})
+ list(APPEND LIBS ${X11_Xinerama_LIB})
+endif()
+
+if(X11_Xdamage_FOUND)
+ add_definitions(-DWITH_XDAMAGE)
+ include_directories(${X11_Xdamage_INCLUDE_PATH})
+ list(APPEND LIBS ${X11_Xdamage_LIB})
+endif()
+
+if(X11_Xfixes_FOUND)
+ add_definitions(-DWITH_XFIXES)
+ include_directories(${X11_Xfixes_INCLUDE_PATH})
+ list(APPEND LIBS ${X11_Xfixes_LIB})
+endif()
+
+if(X11_XTest_FOUND)
+ add_definitions(-DWITH_XTEST)
+ include_directories(${X11_XTest_INCLUDE_PATH})
+ list(APPEND LIBS ${X11_XTest_LIB})
+endif()
+
+# XCursor and XRandr are currently not used so don't link them
+#if(X11_Xcursor_FOUND)
+# add_definitions(-DWITH_XCURSOR)
+# include_directories(${X11_Xcursor_INCLUDE_PATH})
+# list(APPEND LIBS ${X11_Xcursor_LIB})
+#endif()
+
+#if(X11_Xrandr_FOUND)
+# add_definitions(-DWITH_XRANDR)
+# include_directories(${X11_Xrandr_INCLUDE_PATH})
+# list(APPEND LIBS ${X11_Xrandr_LIB})
+#endif()
+
+find_package(PAM)
+if(PAM_FOUND)
+ add_definitions(-DWITH_PAM)
+ include_directories(${PAM_INCLUDE_DIR})
+ list(APPEND LIBS ${PAM_LIBRARY})
+else()
+ message("building without PAM authentication support")
+endif()
+
+add_definitions(-DWITH_SHADOW_X11)
+add_library(freerdp-shadow-subsystem-impl STATIC
+ x11_shadow.h
+ x11_shadow.c
+)
+target_link_libraries(freerdp-shadow-subsystem-impl PRIVATE
+ ${LIBS}
+)
diff --git a/server/shadow/X11/x11_shadow.c b/server/shadow/X11/x11_shadow.c
new file mode 100644
index 0000000..5c1fab1
--- /dev/null
+++ b/server/shadow/X11/x11_shadow.c
@@ -0,0 +1,1513 @@
+/**
+ * FreeRDP: A Remote Desktop Protocol Implementation
+ *
+ * Copyright 2011-2014 Marc-Andre Moreau <marcandre.moreau@gmail.com>
+ * Copyright 2017 Armin Novak <armin.novak@thincast.com>
+ * Copyright 2017 Thincast Technologies GmbH
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <sys/ipc.h>
+#include <sys/shm.h>
+#include <sys/select.h>
+#include <sys/signal.h>
+
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+
+#include <winpr/crt.h>
+#include <winpr/assert.h>
+#include <winpr/path.h>
+#include <winpr/synch.h>
+#include <winpr/image.h>
+#include <winpr/sysinfo.h>
+
+#include <freerdp/log.h>
+#include <freerdp/codec/color.h>
+#include <freerdp/codec/region.h>
+
+#include "x11_shadow.h"
+
+#define TAG SERVER_TAG("shadow.x11")
+
+static UINT32 x11_shadow_enum_monitors(MONITOR_DEF* monitors, UINT32 maxMonitors);
+
+#ifdef WITH_PAM
+
+#include <security/pam_appl.h>
+
+typedef struct
+{
+ const char* user;
+ const char* domain;
+ const char* password;
+} SHADOW_PAM_AUTH_DATA;
+
+typedef struct
+{
+ char* service_name;
+ pam_handle_t* handle;
+ struct pam_conv pamc;
+ SHADOW_PAM_AUTH_DATA appdata;
+} SHADOW_PAM_AUTH_INFO;
+
+static int x11_shadow_pam_conv(int num_msg, const struct pam_message** msg,
+ struct pam_response** resp, void* appdata_ptr)
+{
+ int pam_status = PAM_CONV_ERR;
+ SHADOW_PAM_AUTH_DATA* appdata = NULL;
+ struct pam_response* response = NULL;
+ WINPR_ASSERT(num_msg >= 0);
+ appdata = (SHADOW_PAM_AUTH_DATA*)appdata_ptr;
+ WINPR_ASSERT(appdata);
+
+ if (!(response = (struct pam_response*)calloc((size_t)num_msg, sizeof(struct pam_response))))
+ return PAM_BUF_ERR;
+
+ for (int index = 0; index < num_msg; index++)
+ {
+ switch (msg[index]->msg_style)
+ {
+ case PAM_PROMPT_ECHO_ON:
+ response[index].resp = _strdup(appdata->user);
+
+ if (!response[index].resp)
+ goto out_fail;
+
+ response[index].resp_retcode = PAM_SUCCESS;
+ break;
+
+ case PAM_PROMPT_ECHO_OFF:
+ response[index].resp = _strdup(appdata->password);
+
+ if (!response[index].resp)
+ goto out_fail;
+
+ response[index].resp_retcode = PAM_SUCCESS;
+ break;
+
+ default:
+ pam_status = PAM_CONV_ERR;
+ goto out_fail;
+ }
+ }
+
+ *resp = response;
+ return PAM_SUCCESS;
+out_fail:
+
+ for (int index = 0; index < num_msg; ++index)
+ {
+ if (response[index].resp)
+ {
+ memset(response[index].resp, 0, strlen(response[index].resp));
+ free(response[index].resp);
+ }
+ }
+
+ memset(response, 0, sizeof(struct pam_response) * (size_t)num_msg);
+ free(response);
+ *resp = NULL;
+ return pam_status;
+}
+
+static BOOL x11_shadow_pam_get_service_name(SHADOW_PAM_AUTH_INFO* info)
+{
+ const char* base = "/etc/pam.d";
+ const char* hints[] = { "lightdm", "gdm", "xdm", "login", "sshd" };
+
+ for (size_t x = 0; x < ARRAYSIZE(hints); x++)
+ {
+ char path[MAX_PATH];
+ const char* hint = hints[x];
+
+ _snprintf(path, sizeof(path), "%s/%s", base, hint);
+ if (winpr_PathFileExists(path))
+ {
+
+ info->service_name = _strdup(hint);
+ return info->service_name != NULL;
+ }
+ }
+ WLog_WARN(TAG, "Could not determine PAM service name");
+ return FALSE;
+}
+
+static int x11_shadow_pam_authenticate(rdpShadowSubsystem* subsystem, rdpShadowClient* client,
+ const char* user, const char* domain, const char* password)
+{
+ int pam_status = 0;
+ SHADOW_PAM_AUTH_INFO info = { 0 };
+ WINPR_UNUSED(subsystem);
+ WINPR_UNUSED(client);
+
+ if (!x11_shadow_pam_get_service_name(&info))
+ return -1;
+
+ info.appdata.user = user;
+ info.appdata.domain = domain;
+ info.appdata.password = password;
+ info.pamc.conv = &x11_shadow_pam_conv;
+ info.pamc.appdata_ptr = &info.appdata;
+ pam_status = pam_start(info.service_name, 0, &info.pamc, &info.handle);
+
+ if (pam_status != PAM_SUCCESS)
+ {
+ WLog_ERR(TAG, "pam_start failure: %s", pam_strerror(info.handle, pam_status));
+ return -1;
+ }
+
+ pam_status = pam_authenticate(info.handle, 0);
+
+ if (pam_status != PAM_SUCCESS)
+ {
+ WLog_ERR(TAG, "pam_authenticate failure: %s", pam_strerror(info.handle, pam_status));
+ return -1;
+ }
+
+ pam_status = pam_acct_mgmt(info.handle, 0);
+
+ if (pam_status != PAM_SUCCESS)
+ {
+ WLog_ERR(TAG, "pam_acct_mgmt failure: %s", pam_strerror(info.handle, pam_status));
+ return -1;
+ }
+
+ return 1;
+}
+
+#endif
+
+static BOOL x11_shadow_input_synchronize_event(rdpShadowSubsystem* subsystem,
+ rdpShadowClient* client, UINT32 flags)
+{
+ /* TODO: Implement */
+ WLog_WARN(TAG, "not implemented");
+ return TRUE;
+}
+
+static BOOL x11_shadow_input_keyboard_event(rdpShadowSubsystem* subsystem, rdpShadowClient* client,
+ UINT16 flags, UINT8 code)
+{
+#ifdef WITH_XTEST
+ x11ShadowSubsystem* x11 = (x11ShadowSubsystem*)subsystem;
+ DWORD vkcode = 0;
+ DWORD keycode = 0;
+ DWORD scancode = 0;
+ BOOL extended = FALSE;
+
+ if (!client || !subsystem)
+ return FALSE;
+
+ if (flags & KBD_FLAGS_EXTENDED)
+ extended = TRUE;
+
+ scancode = code;
+ if (extended)
+ scancode |= KBDEXT;
+
+ vkcode = GetVirtualKeyCodeFromVirtualScanCode(scancode, WINPR_KBD_TYPE_IBM_ENHANCED);
+
+ if (extended)
+ vkcode |= KBDEXT;
+
+ keycode = GetKeycodeFromVirtualKeyCode(vkcode, WINPR_KEYCODE_TYPE_XKB);
+
+ if (keycode != 0)
+ {
+ XLockDisplay(x11->display);
+ XTestGrabControl(x11->display, True);
+
+ if (flags & KBD_FLAGS_RELEASE)
+ XTestFakeKeyEvent(x11->display, keycode, False, CurrentTime);
+ else
+ XTestFakeKeyEvent(x11->display, keycode, True, CurrentTime);
+
+ XTestGrabControl(x11->display, False);
+ XFlush(x11->display);
+ XUnlockDisplay(x11->display);
+ }
+
+#endif
+ return TRUE;
+}
+
+static BOOL x11_shadow_input_unicode_keyboard_event(rdpShadowSubsystem* subsystem,
+ rdpShadowClient* client, UINT16 flags,
+ UINT16 code)
+{
+ /* TODO: Implement */
+ WLog_WARN(TAG, "not implemented");
+ return TRUE;
+}
+
+static BOOL x11_shadow_input_mouse_event(rdpShadowSubsystem* subsystem, rdpShadowClient* client,
+ UINT16 flags, UINT16 x, UINT16 y)
+{
+#ifdef WITH_XTEST
+ x11ShadowSubsystem* x11 = (x11ShadowSubsystem*)subsystem;
+ unsigned int button = 0;
+ BOOL down = FALSE;
+ rdpShadowServer* server = NULL;
+ rdpShadowSurface* surface = NULL;
+
+ if (!subsystem || !client)
+ return FALSE;
+
+ server = subsystem->server;
+
+ if (!server)
+ return FALSE;
+
+ surface = server->surface;
+
+ if (!surface)
+ return FALSE;
+
+ x11->lastMouseClient = client;
+ x += surface->x;
+ y += surface->y;
+ XLockDisplay(x11->display);
+ XTestGrabControl(x11->display, True);
+
+ if (flags & PTR_FLAGS_WHEEL)
+ {
+ BOOL negative = FALSE;
+
+ if (flags & PTR_FLAGS_WHEEL_NEGATIVE)
+ negative = TRUE;
+
+ button = (negative) ? 5 : 4;
+ XTestFakeButtonEvent(x11->display, button, True, (unsigned long)CurrentTime);
+ XTestFakeButtonEvent(x11->display, button, False, (unsigned long)CurrentTime);
+ }
+ else if (flags & PTR_FLAGS_HWHEEL)
+ {
+ BOOL negative = FALSE;
+
+ if (flags & PTR_FLAGS_WHEEL_NEGATIVE)
+ negative = TRUE;
+
+ button = (negative) ? 7 : 6;
+ XTestFakeButtonEvent(x11->display, button, True, (unsigned long)CurrentTime);
+ XTestFakeButtonEvent(x11->display, button, False, (unsigned long)CurrentTime);
+ }
+ else
+ {
+ if (flags & PTR_FLAGS_MOVE)
+ XTestFakeMotionEvent(x11->display, 0, x, y, CurrentTime);
+
+ if (flags & PTR_FLAGS_BUTTON1)
+ button = 1;
+ else if (flags & PTR_FLAGS_BUTTON2)
+ button = 3;
+ else if (flags & PTR_FLAGS_BUTTON3)
+ button = 2;
+
+ if (flags & PTR_FLAGS_DOWN)
+ down = TRUE;
+
+ if (button)
+ XTestFakeButtonEvent(x11->display, button, down, CurrentTime);
+ }
+
+ XTestGrabControl(x11->display, False);
+ XFlush(x11->display);
+ XUnlockDisplay(x11->display);
+#endif
+ return TRUE;
+}
+
+static BOOL x11_shadow_input_extended_mouse_event(rdpShadowSubsystem* subsystem,
+ rdpShadowClient* client, UINT16 flags, UINT16 x,
+ UINT16 y)
+{
+#ifdef WITH_XTEST
+ x11ShadowSubsystem* x11 = (x11ShadowSubsystem*)subsystem;
+ UINT button = 0;
+ BOOL down = FALSE;
+ rdpShadowServer* server = NULL;
+ rdpShadowSurface* surface = NULL;
+
+ if (!subsystem || !client)
+ return FALSE;
+
+ server = subsystem->server;
+
+ if (!server)
+ return FALSE;
+
+ surface = server->surface;
+
+ if (!surface)
+ return FALSE;
+
+ x11->lastMouseClient = client;
+ x += surface->x;
+ y += surface->y;
+ XLockDisplay(x11->display);
+ XTestGrabControl(x11->display, True);
+ XTestFakeMotionEvent(x11->display, 0, x, y, CurrentTime);
+
+ if (flags & PTR_XFLAGS_BUTTON1)
+ button = 8;
+ else if (flags & PTR_XFLAGS_BUTTON2)
+ button = 9;
+
+ if (flags & PTR_XFLAGS_DOWN)
+ down = TRUE;
+
+ if (button)
+ XTestFakeButtonEvent(x11->display, button, down, CurrentTime);
+
+ XTestGrabControl(x11->display, False);
+ XFlush(x11->display);
+ XUnlockDisplay(x11->display);
+#endif
+ return TRUE;
+}
+
+static void x11_shadow_message_free(UINT32 id, SHADOW_MSG_OUT* msg)
+{
+ switch (id)
+ {
+ case SHADOW_MSG_OUT_POINTER_POSITION_UPDATE_ID:
+ free(msg);
+ break;
+
+ case SHADOW_MSG_OUT_POINTER_ALPHA_UPDATE_ID:
+ free(((SHADOW_MSG_OUT_POINTER_ALPHA_UPDATE*)msg)->xorMaskData);
+ free(((SHADOW_MSG_OUT_POINTER_ALPHA_UPDATE*)msg)->andMaskData);
+ free(msg);
+ break;
+
+ default:
+ WLog_ERR(TAG, "Unknown message id: %" PRIu32 "", id);
+ free(msg);
+ break;
+ }
+}
+
+static int x11_shadow_pointer_position_update(x11ShadowSubsystem* subsystem)
+{
+ UINT32 msgId = SHADOW_MSG_OUT_POINTER_POSITION_UPDATE_ID;
+ rdpShadowServer* server = NULL;
+ SHADOW_MSG_OUT_POINTER_POSITION_UPDATE templateMsg;
+ int count = 0;
+
+ if (!subsystem || !subsystem->common.server || !subsystem->common.server->clients)
+ return -1;
+
+ templateMsg.xPos = subsystem->common.pointerX;
+ templateMsg.yPos = subsystem->common.pointerY;
+ templateMsg.common.Free = x11_shadow_message_free;
+ server = subsystem->common.server;
+ ArrayList_Lock(server->clients);
+
+ for (size_t index = 0; index < ArrayList_Count(server->clients); index++)
+ {
+ SHADOW_MSG_OUT_POINTER_POSITION_UPDATE* msg = NULL;
+ rdpShadowClient* client = (rdpShadowClient*)ArrayList_GetItem(server->clients, index);
+
+ /* Skip the client which send us the latest mouse event */
+ if (client == subsystem->lastMouseClient)
+ continue;
+
+ msg = malloc(sizeof(templateMsg));
+
+ if (!msg)
+ {
+ count = -1;
+ break;
+ }
+
+ memcpy(msg, &templateMsg, sizeof(templateMsg));
+
+ if (shadow_client_post_msg(client, NULL, msgId, (SHADOW_MSG_OUT*)msg, NULL))
+ count++;
+ }
+
+ ArrayList_Unlock(server->clients);
+ return count;
+}
+
+static int x11_shadow_pointer_alpha_update(x11ShadowSubsystem* subsystem)
+{
+ SHADOW_MSG_OUT_POINTER_ALPHA_UPDATE* msg = NULL;
+ UINT32 msgId = SHADOW_MSG_OUT_POINTER_ALPHA_UPDATE_ID;
+ msg = (SHADOW_MSG_OUT_POINTER_ALPHA_UPDATE*)calloc(1,
+ sizeof(SHADOW_MSG_OUT_POINTER_ALPHA_UPDATE));
+
+ if (!msg)
+ return -1;
+
+ msg->xHot = subsystem->cursorHotX;
+ msg->yHot = subsystem->cursorHotY;
+ msg->width = subsystem->cursorWidth;
+ msg->height = subsystem->cursorHeight;
+
+ if (shadow_subsystem_pointer_convert_alpha_pointer_data(subsystem->cursorPixels, TRUE,
+ msg->width, msg->height, msg) < 0)
+ {
+ free(msg);
+ return -1;
+ }
+
+ msg->common.Free = x11_shadow_message_free;
+ return shadow_client_boardcast_msg(subsystem->common.server, NULL, msgId, (SHADOW_MSG_OUT*)msg,
+ NULL)
+ ? 1
+ : -1;
+}
+
+static int x11_shadow_query_cursor(x11ShadowSubsystem* subsystem, BOOL getImage)
+{
+ int x = 0;
+ int y = 0;
+ int n = 0;
+ rdpShadowServer* server = NULL;
+ rdpShadowSurface* surface = NULL;
+ server = subsystem->common.server;
+ surface = server->surface;
+
+ if (getImage)
+ {
+#ifdef WITH_XFIXES
+ UINT32* pDstPixel = NULL;
+ XFixesCursorImage* ci = NULL;
+ XLockDisplay(subsystem->display);
+ ci = XFixesGetCursorImage(subsystem->display);
+ XUnlockDisplay(subsystem->display);
+
+ if (!ci)
+ return -1;
+
+ x = ci->x;
+ y = ci->y;
+
+ if (ci->width > subsystem->cursorMaxWidth)
+ return -1;
+
+ if (ci->height > subsystem->cursorMaxHeight)
+ return -1;
+
+ subsystem->cursorHotX = ci->xhot;
+ subsystem->cursorHotY = ci->yhot;
+ subsystem->cursorWidth = ci->width;
+ subsystem->cursorHeight = ci->height;
+ subsystem->cursorId = ci->cursor_serial;
+ n = ci->width * ci->height;
+ pDstPixel = (UINT32*)subsystem->cursorPixels;
+
+ for (int k = 0; k < n; k++)
+ {
+ /* XFixesCursorImage.pixels is in *unsigned long*, which may be 8 bytes */
+ *pDstPixel++ = (UINT32)ci->pixels[k];
+ }
+
+ XFree(ci);
+ x11_shadow_pointer_alpha_update(subsystem);
+#endif
+ }
+ else
+ {
+ UINT32 mask = 0;
+ int win_x = 0;
+ int win_y = 0;
+ int root_x = 0;
+ int root_y = 0;
+ Window root = 0;
+ Window child = 0;
+ XLockDisplay(subsystem->display);
+
+ if (!XQueryPointer(subsystem->display, subsystem->root_window, &root, &child, &root_x,
+ &root_y, &win_x, &win_y, &mask))
+ {
+ XUnlockDisplay(subsystem->display);
+ return -1;
+ }
+
+ XUnlockDisplay(subsystem->display);
+ x = root_x;
+ y = root_y;
+ }
+
+ /* Convert to offset based on current surface */
+ if (surface)
+ {
+ x -= surface->x;
+ y -= surface->y;
+ }
+
+ if ((x != (INT64)subsystem->common.pointerX) || (y != (INT64)subsystem->common.pointerY))
+ {
+ subsystem->common.pointerX = x;
+ subsystem->common.pointerY = y;
+ x11_shadow_pointer_position_update(subsystem);
+ }
+
+ return 1;
+}
+
+static int x11_shadow_handle_xevent(x11ShadowSubsystem* subsystem, XEvent* xevent)
+{
+ if (xevent->type == MotionNotify)
+ {
+ }
+
+#ifdef WITH_XFIXES
+ else if (xevent->type == subsystem->xfixes_cursor_notify_event)
+ {
+ x11_shadow_query_cursor(subsystem, TRUE);
+ }
+
+#endif
+ else
+ {
+ }
+
+ return 1;
+}
+
+static void x11_shadow_validate_region(x11ShadowSubsystem* subsystem, int x, int y, int width,
+ int height)
+{
+ XRectangle region;
+
+ if (!subsystem->use_xfixes || !subsystem->use_xdamage)
+ return;
+
+ region.x = x;
+ region.y = y;
+ region.width = width;
+ region.height = height;
+#if defined(WITH_XFIXES) && defined(WITH_XDAMAGE)
+ XLockDisplay(subsystem->display);
+ XFixesSetRegion(subsystem->display, subsystem->xdamage_region, &region, 1);
+ XDamageSubtract(subsystem->display, subsystem->xdamage, subsystem->xdamage_region, None);
+ XUnlockDisplay(subsystem->display);
+#endif
+}
+
+static int x11_shadow_blend_cursor(x11ShadowSubsystem* subsystem)
+{
+ UINT32 nXSrc = 0;
+ UINT32 nYSrc = 0;
+ INT64 nXDst = 0;
+ INT64 nYDst = 0;
+ UINT32 nWidth = 0;
+ UINT32 nHeight = 0;
+ UINT32 nSrcStep = 0;
+ UINT32 nDstStep = 0;
+ BYTE* pSrcData = NULL;
+ BYTE* pDstData = NULL;
+ BYTE A = 0;
+ BYTE R = 0;
+ BYTE G = 0;
+ BYTE B = 0;
+ rdpShadowSurface* surface = NULL;
+
+ if (!subsystem)
+ return -1;
+
+ surface = subsystem->common.server->surface;
+ nXSrc = 0;
+ nYSrc = 0;
+ nWidth = subsystem->cursorWidth;
+ nHeight = subsystem->cursorHeight;
+ nXDst = subsystem->common.pointerX - subsystem->cursorHotX;
+ nYDst = subsystem->common.pointerY - subsystem->cursorHotY;
+
+ if (nXDst >= surface->width)
+ return 1;
+
+ if (nXDst < 0)
+ {
+ nXDst *= -1;
+
+ if (nXDst >= nWidth)
+ return 1;
+
+ nXSrc = (UINT32)nXDst;
+ nWidth -= nXDst;
+ nXDst = 0;
+ }
+
+ if (nYDst >= surface->height)
+ return 1;
+
+ if (nYDst < 0)
+ {
+ nYDst *= -1;
+
+ if (nYDst >= nHeight)
+ return 1;
+
+ nYSrc = (UINT32)nYDst;
+ nHeight -= nYDst;
+ nYDst = 0;
+ }
+
+ if ((nXDst + nWidth) > surface->width)
+ nWidth = surface->width - nXDst;
+
+ if ((nYDst + nHeight) > surface->height)
+ nHeight = surface->height - nYDst;
+
+ pSrcData = subsystem->cursorPixels;
+ nSrcStep = subsystem->cursorWidth * 4;
+ pDstData = surface->data;
+ nDstStep = surface->scanline;
+
+ for (int y = 0; y < nHeight; y++)
+ {
+ const BYTE* pSrcPixel = &pSrcData[((nYSrc + y) * nSrcStep) + (nXSrc * 4)];
+ BYTE* pDstPixel = &pDstData[((nYDst + y) * nDstStep) + (nXDst * 4)];
+
+ for (int x = 0; x < nWidth; x++)
+ {
+ B = *pSrcPixel++;
+ G = *pSrcPixel++;
+ R = *pSrcPixel++;
+ A = *pSrcPixel++;
+
+ if (A == 0xFF)
+ {
+ pDstPixel[0] = B;
+ pDstPixel[1] = G;
+ pDstPixel[2] = R;
+ }
+ else
+ {
+ pDstPixel[0] = B + (pDstPixel[0] * (0xFF - A) + (0xFF / 2)) / 0xFF;
+ pDstPixel[1] = G + (pDstPixel[1] * (0xFF - A) + (0xFF / 2)) / 0xFF;
+ pDstPixel[2] = R + (pDstPixel[2] * (0xFF - A) + (0xFF / 2)) / 0xFF;
+ }
+
+ pDstPixel[3] = 0xFF;
+ pDstPixel += 4;
+ }
+ }
+
+ return 1;
+}
+
+static BOOL x11_shadow_check_resize(x11ShadowSubsystem* subsystem)
+{
+ XWindowAttributes attr;
+ XLockDisplay(subsystem->display);
+ XGetWindowAttributes(subsystem->display, subsystem->root_window, &attr);
+ XUnlockDisplay(subsystem->display);
+
+ if (attr.width != (INT64)subsystem->width || attr.height != (INT64)subsystem->height)
+ {
+ MONITOR_DEF* virtualScreen = &(subsystem->common.virtualScreen);
+
+ /* Screen size changed. Refresh monitor definitions and trigger screen resize */
+ subsystem->common.numMonitors = x11_shadow_enum_monitors(subsystem->common.monitors, 16);
+ shadow_screen_resize(subsystem->common.server->screen);
+ subsystem->width = attr.width;
+ subsystem->height = attr.height;
+
+ virtualScreen->left = 0;
+ virtualScreen->top = 0;
+ virtualScreen->right = subsystem->width - 1;
+ virtualScreen->bottom = subsystem->height - 1;
+ virtualScreen->flags = 1;
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static int x11_shadow_error_handler_for_capture(Display* display, XErrorEvent* event)
+{
+ char msg[256];
+ XGetErrorText(display, event->error_code, (char*)&msg, sizeof(msg));
+ WLog_ERR(TAG, "X11 error: %s Error code: %x, request code: %x, minor code: %x", msg,
+ event->error_code, event->request_code, event->minor_code);
+
+ /* Ignore BAD MATCH error during image capture. Abort in other case */
+ if (event->error_code != BadMatch)
+ {
+ abort();
+ }
+
+ return 0;
+}
+
+static int x11_shadow_screen_grab(x11ShadowSubsystem* subsystem)
+{
+ int rc = 0;
+ size_t count = 0;
+ int status = -1;
+ int x = 0;
+ int y = 0;
+ int width = 0;
+ int height = 0;
+ XImage* image = NULL;
+ rdpShadowServer* server = NULL;
+ rdpShadowSurface* surface = NULL;
+ RECTANGLE_16 invalidRect;
+ RECTANGLE_16 surfaceRect;
+ const RECTANGLE_16* extents = NULL;
+ server = subsystem->common.server;
+ surface = server->surface;
+ count = ArrayList_Count(server->clients);
+
+ if (count < 1)
+ return 1;
+
+ EnterCriticalSection(&surface->lock);
+ surfaceRect.left = 0;
+ surfaceRect.top = 0;
+ surfaceRect.right = surface->width;
+ surfaceRect.bottom = surface->height;
+ LeaveCriticalSection(&surface->lock);
+
+ XLockDisplay(subsystem->display);
+ /*
+ * Ignore BadMatch error during image capture. The screen size may be
+ * changed outside. We will resize to correct resolution at next frame
+ */
+ XSetErrorHandler(x11_shadow_error_handler_for_capture);
+#if defined(WITH_XDAMAGE)
+ if (subsystem->use_xshm)
+ {
+ image = subsystem->fb_image;
+ XCopyArea(subsystem->display, subsystem->root_window, subsystem->fb_pixmap,
+ subsystem->xshm_gc, 0, 0, subsystem->width, subsystem->height, 0, 0);
+
+ EnterCriticalSection(&surface->lock);
+ status = shadow_capture_compare(surface->data, surface->scanline, surface->width,
+ surface->height, (BYTE*)&(image->data[surface->width * 4]),
+ image->bytes_per_line, &invalidRect);
+ LeaveCriticalSection(&surface->lock);
+ }
+ else
+#endif
+ {
+ EnterCriticalSection(&surface->lock);
+ image = XGetImage(subsystem->display, subsystem->root_window, surface->x, surface->y,
+ surface->width, surface->height, AllPlanes, ZPixmap);
+
+ if (image)
+ {
+ status = shadow_capture_compare(surface->data, surface->scanline, surface->width,
+ surface->height, (BYTE*)image->data,
+ image->bytes_per_line, &invalidRect);
+ }
+ LeaveCriticalSection(&surface->lock);
+ if (!image)
+ {
+ /*
+ * BadMatch error happened. The size may have been changed again.
+ * Give up this frame and we will resize again in next frame
+ */
+ goto fail_capture;
+ }
+ }
+
+ /* Restore the default error handler */
+ XSetErrorHandler(NULL);
+ XSync(subsystem->display, False);
+ XUnlockDisplay(subsystem->display);
+
+ if (status)
+ {
+ BOOL empty = 0;
+ EnterCriticalSection(&surface->lock);
+ region16_union_rect(&(surface->invalidRegion), &(surface->invalidRegion), &invalidRect);
+ region16_intersect_rect(&(surface->invalidRegion), &(surface->invalidRegion), &surfaceRect);
+ empty = region16_is_empty(&(surface->invalidRegion));
+ LeaveCriticalSection(&surface->lock);
+
+ if (!empty)
+ {
+ BOOL success = 0;
+ EnterCriticalSection(&surface->lock);
+ extents = region16_extents(&(surface->invalidRegion));
+ x = extents->left;
+ y = extents->top;
+ width = extents->right - extents->left;
+ height = extents->bottom - extents->top;
+ WINPR_ASSERT(image);
+ WINPR_ASSERT(image->bytes_per_line >= 0);
+ WINPR_ASSERT(width >= 0);
+ WINPR_ASSERT(height >= 0);
+ success = freerdp_image_copy(surface->data, surface->format, surface->scanline, x, y,
+ (UINT32)width, (UINT32)height, (BYTE*)image->data,
+ PIXEL_FORMAT_BGRX32, (UINT32)image->bytes_per_line, x, y,
+ NULL, FREERDP_FLIP_NONE);
+ LeaveCriticalSection(&surface->lock);
+ if (!success)
+ goto fail_capture;
+
+ // x11_shadow_blend_cursor(subsystem);
+ count = ArrayList_Count(server->clients);
+ shadow_subsystem_frame_update(&subsystem->common);
+
+ if (count == 1)
+ {
+ rdpShadowClient* client = NULL;
+ client = (rdpShadowClient*)ArrayList_GetItem(server->clients, 0);
+
+ if (client)
+ subsystem->common.captureFrameRate =
+ shadow_encoder_preferred_fps(client->encoder);
+ }
+
+ EnterCriticalSection(&surface->lock);
+ region16_clear(&(surface->invalidRegion));
+ LeaveCriticalSection(&surface->lock);
+ }
+ }
+
+ rc = 1;
+fail_capture:
+ if (!subsystem->use_xshm && image)
+ XDestroyImage(image);
+
+ if (rc != 1)
+ {
+ XSetErrorHandler(NULL);
+ XSync(subsystem->display, False);
+ XUnlockDisplay(subsystem->display);
+ }
+
+ return rc;
+}
+
+static int x11_shadow_subsystem_process_message(x11ShadowSubsystem* subsystem, wMessage* message)
+{
+ switch (message->id)
+ {
+ case SHADOW_MSG_IN_REFRESH_REQUEST_ID:
+ shadow_subsystem_frame_update((rdpShadowSubsystem*)subsystem);
+ break;
+
+ default:
+ WLog_ERR(TAG, "Unknown message id: %" PRIu32 "", message->id);
+ break;
+ }
+
+ if (message->Free)
+ message->Free(message);
+
+ return 1;
+}
+
+static DWORD WINAPI x11_shadow_subsystem_thread(LPVOID arg)
+{
+ x11ShadowSubsystem* subsystem = (x11ShadowSubsystem*)arg;
+ XEvent xevent;
+ DWORD status = 0;
+ DWORD nCount = 0;
+ UINT64 cTime = 0;
+ DWORD dwTimeout = 0;
+ DWORD dwInterval = 0;
+ UINT64 frameTime = 0;
+ HANDLE events[32];
+ wMessage message;
+ wMessagePipe* MsgPipe = NULL;
+ MsgPipe = subsystem->common.MsgPipe;
+ nCount = 0;
+ events[nCount++] = subsystem->common.event;
+ events[nCount++] = MessageQueue_Event(MsgPipe->In);
+ subsystem->common.captureFrameRate = 16;
+ dwInterval = 1000 / subsystem->common.captureFrameRate;
+ frameTime = GetTickCount64() + dwInterval;
+
+ while (1)
+ {
+ cTime = GetTickCount64();
+ dwTimeout = (cTime > frameTime) ? 0 : frameTime - cTime;
+ status = WaitForMultipleObjects(nCount, events, FALSE, dwTimeout);
+
+ if (WaitForSingleObject(MessageQueue_Event(MsgPipe->In), 0) == WAIT_OBJECT_0)
+ {
+ if (MessageQueue_Peek(MsgPipe->In, &message, TRUE))
+ {
+ if (message.id == WMQ_QUIT)
+ break;
+
+ x11_shadow_subsystem_process_message(subsystem, &message);
+ }
+ }
+
+ if (WaitForSingleObject(subsystem->common.event, 0) == WAIT_OBJECT_0)
+ {
+ XLockDisplay(subsystem->display);
+
+ if (XEventsQueued(subsystem->display, QueuedAlready))
+ {
+ XNextEvent(subsystem->display, &xevent);
+ x11_shadow_handle_xevent(subsystem, &xevent);
+ }
+
+ XUnlockDisplay(subsystem->display);
+ }
+
+ if ((status == WAIT_TIMEOUT) || (GetTickCount64() > frameTime))
+ {
+ x11_shadow_check_resize(subsystem);
+ x11_shadow_screen_grab(subsystem);
+ x11_shadow_query_cursor(subsystem, FALSE);
+ dwInterval = 1000 / subsystem->common.captureFrameRate;
+ frameTime += dwInterval;
+ }
+ }
+
+ ExitThread(0);
+ return 0;
+}
+
+static int x11_shadow_subsystem_base_init(x11ShadowSubsystem* subsystem)
+{
+ if (subsystem->display)
+ return 1; /* initialize once */
+
+ if (!getenv("DISPLAY"))
+ setenv("DISPLAY", ":0", 1);
+
+ if (!XInitThreads())
+ return -1;
+
+ subsystem->display = XOpenDisplay(NULL);
+
+ if (!subsystem->display)
+ {
+ WLog_ERR(TAG, "failed to open display: %s", XDisplayName(NULL));
+ return -1;
+ }
+
+ subsystem->xfds = ConnectionNumber(subsystem->display);
+ subsystem->number = DefaultScreen(subsystem->display);
+ subsystem->screen = ScreenOfDisplay(subsystem->display, subsystem->number);
+ subsystem->depth = DefaultDepthOfScreen(subsystem->screen);
+ subsystem->width = WidthOfScreen(subsystem->screen);
+ subsystem->height = HeightOfScreen(subsystem->screen);
+ subsystem->root_window = RootWindow(subsystem->display, subsystem->number);
+ return 1;
+}
+
+static int x11_shadow_xfixes_init(x11ShadowSubsystem* subsystem)
+{
+#ifdef WITH_XFIXES
+ int xfixes_event = 0;
+ int xfixes_error = 0;
+ int major = 0;
+ int minor = 0;
+
+ if (!XFixesQueryExtension(subsystem->display, &xfixes_event, &xfixes_error))
+ return -1;
+
+ if (!XFixesQueryVersion(subsystem->display, &major, &minor))
+ return -1;
+
+ subsystem->xfixes_cursor_notify_event = xfixes_event + XFixesCursorNotify;
+ XFixesSelectCursorInput(subsystem->display, subsystem->root_window,
+ XFixesDisplayCursorNotifyMask);
+ return 1;
+#else
+ return -1;
+#endif
+}
+
+static int x11_shadow_xinerama_init(x11ShadowSubsystem* subsystem)
+{
+#ifdef WITH_XINERAMA
+ int xinerama_event = 0;
+ int xinerama_error = 0;
+ x11_shadow_subsystem_base_init(subsystem);
+
+ if (!XineramaQueryExtension(subsystem->display, &xinerama_event, &xinerama_error))
+ return -1;
+
+#if defined(WITH_XDAMAGE)
+ int major = 0;
+ int minor = 0;
+ if (!XDamageQueryVersion(subsystem->display, &major, &minor))
+ return -1;
+#endif
+
+ if (!XineramaIsActive(subsystem->display))
+ return -1;
+
+ return 1;
+#else
+ return -1;
+#endif
+}
+
+static int x11_shadow_xdamage_init(x11ShadowSubsystem* subsystem)
+{
+#ifdef WITH_XDAMAGE
+ int major = 0;
+ int minor = 0;
+ int damage_event = 0;
+ int damage_error = 0;
+
+ if (!subsystem->use_xfixes)
+ return -1;
+
+ if (!XDamageQueryExtension(subsystem->display, &damage_event, &damage_error))
+ return -1;
+
+ if (!XDamageQueryVersion(subsystem->display, &major, &minor))
+ return -1;
+
+ if (major < 1)
+ return -1;
+
+ subsystem->xdamage_notify_event = damage_event + XDamageNotify;
+ subsystem->xdamage =
+ XDamageCreate(subsystem->display, subsystem->root_window, XDamageReportDeltaRectangles);
+
+ if (!subsystem->xdamage)
+ return -1;
+
+#ifdef WITH_XFIXES
+ subsystem->xdamage_region = XFixesCreateRegion(subsystem->display, NULL, 0);
+
+ if (!subsystem->xdamage_region)
+ return -1;
+
+#endif
+ return 1;
+#else
+ return -1;
+#endif
+}
+
+static int x11_shadow_xshm_init(x11ShadowSubsystem* subsystem)
+{
+ Bool pixmaps = 0;
+ int major = 0;
+ int minor = 0;
+ XGCValues values;
+
+ if (!XShmQueryExtension(subsystem->display))
+ return -1;
+
+ if (!XShmQueryVersion(subsystem->display, &major, &minor, &pixmaps))
+ return -1;
+
+ if (!pixmaps)
+ return -1;
+
+ subsystem->fb_shm_info.shmid = -1;
+ subsystem->fb_shm_info.shmaddr = (char*)-1;
+ subsystem->fb_shm_info.readOnly = False;
+ subsystem->fb_image =
+ XShmCreateImage(subsystem->display, subsystem->visual, subsystem->depth, ZPixmap, NULL,
+ &(subsystem->fb_shm_info), subsystem->width, subsystem->height);
+
+ if (!subsystem->fb_image)
+ {
+ WLog_ERR(TAG, "XShmCreateImage failed");
+ return -1;
+ }
+
+ subsystem->fb_shm_info.shmid = shmget(
+ IPC_PRIVATE, 1ull * subsystem->fb_image->bytes_per_line * subsystem->fb_image->height,
+ IPC_CREAT | 0600);
+
+ if (subsystem->fb_shm_info.shmid == -1)
+ {
+ WLog_ERR(TAG, "shmget failed");
+ return -1;
+ }
+
+ subsystem->fb_shm_info.shmaddr = shmat(subsystem->fb_shm_info.shmid, 0, 0);
+ subsystem->fb_image->data = subsystem->fb_shm_info.shmaddr;
+
+ if (subsystem->fb_shm_info.shmaddr == ((char*)-1))
+ {
+ WLog_ERR(TAG, "shmat failed");
+ return -1;
+ }
+
+ if (!XShmAttach(subsystem->display, &(subsystem->fb_shm_info)))
+ return -1;
+
+ XSync(subsystem->display, False);
+ shmctl(subsystem->fb_shm_info.shmid, IPC_RMID, 0);
+ subsystem->fb_pixmap =
+ XShmCreatePixmap(subsystem->display, subsystem->root_window, subsystem->fb_image->data,
+ &(subsystem->fb_shm_info), subsystem->fb_image->width,
+ subsystem->fb_image->height, subsystem->fb_image->depth);
+ XSync(subsystem->display, False);
+
+ if (!subsystem->fb_pixmap)
+ return -1;
+
+ values.subwindow_mode = IncludeInferiors;
+ values.graphics_exposures = False;
+#if defined(WITH_XDAMAGE)
+ subsystem->xshm_gc = XCreateGC(subsystem->display, subsystem->root_window,
+ GCSubwindowMode | GCGraphicsExposures, &values);
+ XSetFunction(subsystem->display, subsystem->xshm_gc, GXcopy);
+#endif
+ XSync(subsystem->display, False);
+ return 1;
+}
+
+UINT32 x11_shadow_enum_monitors(MONITOR_DEF* monitors, UINT32 maxMonitors)
+{
+ Display* display = NULL;
+ int displayWidth = 0;
+ int displayHeight = 0;
+ int numMonitors = 0;
+
+ if (!getenv("DISPLAY"))
+ setenv("DISPLAY", ":0", 1);
+
+ display = XOpenDisplay(NULL);
+
+ if (!display)
+ {
+ WLog_ERR(TAG, "failed to open display: %s", XDisplayName(NULL));
+ return -1;
+ }
+
+ displayWidth = WidthOfScreen(DefaultScreenOfDisplay(display));
+ displayHeight = HeightOfScreen(DefaultScreenOfDisplay(display));
+#ifdef WITH_XINERAMA
+ {
+#if defined(WITH_XDAMAGE)
+ int major = 0;
+ int minor = 0;
+#endif
+ int xinerama_event = 0;
+ int xinerama_error = 0;
+ XineramaScreenInfo* screens = NULL;
+
+ const Bool xinerama = XineramaQueryExtension(display, &xinerama_event, &xinerama_error);
+ const Bool damage =
+#if defined(WITH_XDAMAGE)
+ XDamageQueryVersion(display, &major, &minor);
+#else
+ False;
+#endif
+
+ if (xinerama && damage && XineramaIsActive(display))
+ {
+ screens = XineramaQueryScreens(display, &numMonitors);
+
+ if (numMonitors > (INT64)maxMonitors)
+ numMonitors = (int)maxMonitors;
+
+ if (screens && (numMonitors > 0))
+ {
+ for (int index = 0; index < numMonitors; index++)
+ {
+ MONITOR_DEF* monitor = &monitors[index];
+ const XineramaScreenInfo* screen = &screens[index];
+
+ monitor->left = screen->x_org;
+ monitor->top = screen->y_org;
+ monitor->right = monitor->left + screen->width - 1;
+ monitor->bottom = monitor->top + screen->height - 1;
+ monitor->flags = (index == 0) ? 1 : 0;
+ }
+ }
+
+ XFree(screens);
+ }
+ }
+#endif
+ XCloseDisplay(display);
+
+ if (numMonitors < 1)
+ {
+ MONITOR_DEF* monitor = &monitors[0];
+ numMonitors = 1;
+
+ monitor->left = 0;
+ monitor->top = 0;
+ monitor->right = displayWidth - 1;
+ monitor->bottom = displayHeight - 1;
+ monitor->flags = 1;
+ }
+
+ errno = 0;
+ return numMonitors;
+}
+
+static int x11_shadow_subsystem_init(rdpShadowSubsystem* sub)
+{
+ int pf_count = 0;
+ int vi_count = 0;
+ int nextensions = 0;
+ char** extensions = NULL;
+ XVisualInfo* vi = NULL;
+ XVisualInfo* vis = NULL;
+ XVisualInfo template = { 0 };
+ XPixmapFormatValues* pf = NULL;
+ XPixmapFormatValues* pfs = NULL;
+
+ x11ShadowSubsystem* subsystem = (x11ShadowSubsystem*)sub;
+
+ if (!subsystem)
+ return -1;
+
+ subsystem->common.numMonitors = x11_shadow_enum_monitors(subsystem->common.monitors, 16);
+ x11_shadow_subsystem_base_init(subsystem);
+
+ if ((subsystem->depth != 24) && (subsystem->depth != 32))
+ {
+ WLog_ERR(TAG, "unsupported X11 server color depth: %" PRIu32, subsystem->depth);
+ return -1;
+ }
+
+ extensions = XListExtensions(subsystem->display, &nextensions);
+
+ if (!extensions || (nextensions < 0))
+ return -1;
+
+ for (int i = 0; i < nextensions; i++)
+ {
+ if (strcmp(extensions[i], "Composite") == 0)
+ subsystem->composite = TRUE;
+ }
+
+ XFreeExtensionList(extensions);
+
+ if (subsystem->composite)
+ subsystem->use_xdamage = FALSE;
+
+ pfs = XListPixmapFormats(subsystem->display, &pf_count);
+
+ if (!pfs)
+ {
+ WLog_ERR(TAG, "XListPixmapFormats failed");
+ return -1;
+ }
+
+ for (int i = 0; i < pf_count; i++)
+ {
+ pf = pfs + i;
+
+ if (pf->depth == (INT64)subsystem->depth)
+ {
+ subsystem->bpp = pf->bits_per_pixel;
+ subsystem->scanline_pad = pf->scanline_pad;
+ break;
+ }
+ }
+
+ XFree(pfs);
+ template.class = TrueColor;
+ template.screen = subsystem->number;
+ vis = XGetVisualInfo(subsystem->display, VisualClassMask | VisualScreenMask, &template,
+ &vi_count);
+
+ if (!vis)
+ {
+ WLog_ERR(TAG, "XGetVisualInfo failed");
+ return -1;
+ }
+
+ for (int i = 0; i < vi_count; i++)
+ {
+ vi = vis + i;
+
+ if (vi->depth == (INT64)subsystem->depth)
+ {
+ subsystem->visual = vi->visual;
+ break;
+ }
+ }
+
+ XFree(vis);
+ XSelectInput(subsystem->display, subsystem->root_window, SubstructureNotifyMask);
+ subsystem->cursorMaxWidth = 256;
+ subsystem->cursorMaxHeight = 256;
+ subsystem->cursorPixels =
+ winpr_aligned_malloc(subsystem->cursorMaxWidth * subsystem->cursorMaxHeight * 4ull, 16);
+
+ if (!subsystem->cursorPixels)
+ return -1;
+
+ x11_shadow_query_cursor(subsystem, TRUE);
+
+ if (subsystem->use_xfixes)
+ {
+ if (x11_shadow_xfixes_init(subsystem) < 0)
+ subsystem->use_xfixes = FALSE;
+ }
+
+ if (subsystem->use_xinerama)
+ {
+ if (x11_shadow_xinerama_init(subsystem) < 0)
+ subsystem->use_xinerama = FALSE;
+ }
+
+ if (subsystem->use_xshm)
+ {
+ if (x11_shadow_xshm_init(subsystem) < 0)
+ subsystem->use_xshm = FALSE;
+ }
+
+ if (subsystem->use_xdamage)
+ {
+ if (x11_shadow_xdamage_init(subsystem) < 0)
+ subsystem->use_xdamage = FALSE;
+ }
+
+ if (!(subsystem->common.event =
+ CreateFileDescriptorEvent(NULL, FALSE, FALSE, subsystem->xfds, WINPR_FD_READ)))
+ return -1;
+
+ {
+ MONITOR_DEF* virtualScreen = &(subsystem->common.virtualScreen);
+ virtualScreen->left = 0;
+ virtualScreen->top = 0;
+ WINPR_ASSERT(subsystem->width <= INT32_MAX);
+ WINPR_ASSERT(subsystem->height <= INT32_MAX);
+ virtualScreen->right = (INT32)subsystem->width - 1;
+ virtualScreen->bottom = (INT32)subsystem->height - 1;
+ virtualScreen->flags = 1;
+ WLog_INFO(TAG,
+ "X11 Extensions: XFixes: %" PRId32 " Xinerama: %" PRId32 " XDamage: %" PRId32
+ " XShm: %" PRId32 "",
+ subsystem->use_xfixes, subsystem->use_xinerama, subsystem->use_xdamage,
+ subsystem->use_xshm);
+ }
+ return 1;
+}
+
+static int x11_shadow_subsystem_uninit(rdpShadowSubsystem* sub)
+{
+ x11ShadowSubsystem* subsystem = (x11ShadowSubsystem*)sub;
+
+ if (!subsystem)
+ return -1;
+
+ if (subsystem->display)
+ {
+ XCloseDisplay(subsystem->display);
+ subsystem->display = NULL;
+ }
+
+ if (subsystem->common.event)
+ {
+ CloseHandle(subsystem->common.event);
+ subsystem->common.event = NULL;
+ }
+
+ if (subsystem->cursorPixels)
+ {
+ winpr_aligned_free(subsystem->cursorPixels);
+ subsystem->cursorPixels = NULL;
+ }
+
+ return 1;
+}
+
+static int x11_shadow_subsystem_start(rdpShadowSubsystem* sub)
+{
+ x11ShadowSubsystem* subsystem = (x11ShadowSubsystem*)sub;
+
+ if (!subsystem)
+ return -1;
+
+ if (!(subsystem->thread =
+ CreateThread(NULL, 0, x11_shadow_subsystem_thread, (void*)subsystem, 0, NULL)))
+ {
+ WLog_ERR(TAG, "Failed to create thread");
+ return -1;
+ }
+
+ return 1;
+}
+
+static int x11_shadow_subsystem_stop(rdpShadowSubsystem* sub)
+{
+ x11ShadowSubsystem* subsystem = (x11ShadowSubsystem*)sub;
+
+ if (!subsystem)
+ return -1;
+
+ if (subsystem->thread)
+ {
+ if (MessageQueue_PostQuit(subsystem->common.MsgPipe->In, 0))
+ WaitForSingleObject(subsystem->thread, INFINITE);
+
+ CloseHandle(subsystem->thread);
+ subsystem->thread = NULL;
+ }
+
+ return 1;
+}
+
+static rdpShadowSubsystem* x11_shadow_subsystem_new(void)
+{
+ x11ShadowSubsystem* subsystem = NULL;
+ subsystem = (x11ShadowSubsystem*)calloc(1, sizeof(x11ShadowSubsystem));
+
+ if (!subsystem)
+ return NULL;
+
+#ifdef WITH_PAM
+ subsystem->common.Authenticate = x11_shadow_pam_authenticate;
+#endif
+ subsystem->common.SynchronizeEvent = x11_shadow_input_synchronize_event;
+ subsystem->common.KeyboardEvent = x11_shadow_input_keyboard_event;
+ subsystem->common.UnicodeKeyboardEvent = x11_shadow_input_unicode_keyboard_event;
+ subsystem->common.MouseEvent = x11_shadow_input_mouse_event;
+ subsystem->common.ExtendedMouseEvent = x11_shadow_input_extended_mouse_event;
+ subsystem->composite = FALSE;
+ subsystem->use_xshm = FALSE; /* temporarily disabled */
+ subsystem->use_xfixes = TRUE;
+ subsystem->use_xdamage = FALSE;
+ subsystem->use_xinerama = TRUE;
+ return (rdpShadowSubsystem*)subsystem;
+}
+
+static void x11_shadow_subsystem_free(rdpShadowSubsystem* subsystem)
+{
+ if (!subsystem)
+ return;
+
+ x11_shadow_subsystem_uninit(subsystem);
+ free(subsystem);
+}
+
+FREERDP_ENTRY_POINT(FREERDP_API const char* ShadowSubsystemName(void))
+{
+ return "X11";
+}
+
+FREERDP_ENTRY_POINT(FREERDP_API int ShadowSubsystemEntry(RDP_SHADOW_ENTRY_POINTS* pEntryPoints))
+{
+ if (!pEntryPoints)
+ return -1;
+
+ pEntryPoints->New = x11_shadow_subsystem_new;
+ pEntryPoints->Free = x11_shadow_subsystem_free;
+ pEntryPoints->Init = x11_shadow_subsystem_init;
+ pEntryPoints->Uninit = x11_shadow_subsystem_uninit;
+ pEntryPoints->Start = x11_shadow_subsystem_start;
+ pEntryPoints->Stop = x11_shadow_subsystem_stop;
+ pEntryPoints->EnumMonitors = x11_shadow_enum_monitors;
+ return 1;
+}
diff --git a/server/shadow/X11/x11_shadow.h b/server/shadow/X11/x11_shadow.h
new file mode 100644
index 0000000..aca2d63
--- /dev/null
+++ b/server/shadow/X11/x11_shadow.h
@@ -0,0 +1,114 @@
+/**
+ * FreeRDP: A Remote Desktop Protocol Implementation
+ *
+ * Copyright 2011-2014 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.
+ */
+
+#ifndef FREERDP_SERVER_SHADOW_X11_H
+#define FREERDP_SERVER_SHADOW_X11_H
+
+#include <freerdp/server/shadow.h>
+
+typedef struct x11_shadow_subsystem x11ShadowSubsystem;
+
+#include <winpr/crt.h>
+#include <winpr/synch.h>
+#include <winpr/thread.h>
+#include <winpr/stream.h>
+#include <winpr/collections.h>
+
+#include <X11/Xlib.h>
+
+#ifdef WITH_XSHM
+#include <X11/extensions/XShm.h>
+#endif
+
+#ifdef WITH_XFIXES
+#include <X11/extensions/Xfixes.h>
+#endif
+
+#ifdef WITH_XTEST
+#include <X11/extensions/XTest.h>
+#endif
+
+#ifdef WITH_XDAMAGE
+#include <X11/extensions/Xdamage.h>
+#endif
+
+#ifdef WITH_XINERAMA
+#include <X11/extensions/Xinerama.h>
+#endif
+
+struct x11_shadow_subsystem
+{
+ rdpShadowSubsystem common;
+
+ HANDLE thread;
+
+ UINT32 bpp;
+ int xfds;
+ UINT32 depth;
+ UINT32 width;
+ UINT32 height;
+ int number;
+ XImage* image;
+ Screen* screen;
+ Visual* visual;
+ Display* display;
+ UINT32 scanline_pad;
+ BOOL composite;
+
+ BOOL use_xshm;
+ BOOL use_xfixes;
+ BOOL use_xdamage;
+ BOOL use_xinerama;
+
+ XImage* fb_image;
+ Pixmap fb_pixmap;
+ Window root_window;
+ XShmSegmentInfo fb_shm_info;
+
+ UINT32 cursorHotX;
+ UINT32 cursorHotY;
+ UINT32 cursorWidth;
+ UINT32 cursorHeight;
+ UINT32 cursorId;
+ BYTE* cursorPixels;
+ UINT32 cursorMaxWidth;
+ UINT32 cursorMaxHeight;
+ rdpShadowClient* lastMouseClient;
+
+#ifdef WITH_XDAMAGE
+ GC xshm_gc;
+ Damage xdamage;
+ int xdamage_notify_event;
+ XserverRegion xdamage_region;
+#endif
+
+#ifdef WITH_XFIXES
+ int xfixes_cursor_notify_event;
+#endif
+};
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* FREERDP_SERVER_SHADOW_X11_H */
diff --git a/server/shadow/freerdp-shadow-cli.1.in b/server/shadow/freerdp-shadow-cli.1.in
new file mode 100644
index 0000000..890fb7a
--- /dev/null
+++ b/server/shadow/freerdp-shadow-cli.1.in
@@ -0,0 +1,85 @@
+.de URL
+\\$2 \(laURL: \\$1 \(ra\\$3
+..
+.if \n[.g] .mso www.tmac
+.TH @MANPAGE_NAME@ 1 2017-01-12 "@FREERDP_VERSION_FULL@" "FreeRDP"
+.SH NAME
+@MANPAGE_NAME@ \- A utility for sharing a X display via RDP.
+.SH SYNOPSIS
+.B @MANPAGE_NAME@
+[\fB/port:\fP\fI<port number>\fP]
+[\fB/ipc-socket:\fP\fI<ipc-socket>\fP]
+[\fB/monitors:\fP\fI<0,1,2,...>\fP]
+[\fB/rect:\fP\fI<x,y,w,h>\fP]
+[\fB+auth\fP]
+[\fB-may-view\fP]
+[\fB-may-interact\fP]
+[\fB/sec:\fP\fI<rdp|tls|nla|ext>\fP]
+[\fB-sec-rdp\fP]
+[\fB-sec-tls\fP]
+[\fB-sec-nla\fP]
+[\fB-sec-ext\fP]
+[\fB/sam-file:\fP\fI<file>\fP]
+[\fB/version\fP]
+[\fB/help\fP]
+.SH DESCRIPTION
+.B @MANPAGE_NAME@
+can be used to share a running X display like with VNC but by using the RDP
+instead. It is also possibly to share only parts (rect) of the display.
+.SH OPTIONS
+.IP /ipc-socket:<ipc-socket>
+If this option is set an ipc socket with the path \fIipc-socket\fP is used
+instead of a TCP socket.
+.IP /port:<port>
+Set the port to use. Default is 3389.
+This option is ignored if ipc-socket is used.
+.IP /monitors:<1,2,3,...>
+Select the monitor(s) to share.
+.IP /rect:<x,y,w,h>
+Select rectangle within monitor to share.
+.IP -auth
+Disable authentication. If authentication is enabled PAM is used with the
+X11 subsystem. Running as root is not necessary, however if run as user only
+the same user that started @MANPAGE_NAME@ can authenticate.
+.br
+\fBWarning\fP: If authentication is disabled \fIeveryone\fP can connect.
+.IP -may-view
+Clients may view without prompt.
+.IP -may-interact
+Clients may interact without prompt.
+.IP /sec:<rdp|tls|nla|ext>
+Force a specific protocol security
+.IP -sec-rdp
+Disable RDP security (default:on)
+.IP -sec-tls
+Disable TLS protocol security (default:on)
+.IP -sec-nla
+Disable NLA protocol security (default:on)
+.IP +sec-ext
+Use NLA extended protocol security (default:off)
+.IP /sam-file:<file>
+NTLM SAM file for NLA authentication
+.IP /version
+Print the version and exit.
+.IP /help
+Print the help and exit.
+
+.SH EXAMPLES
+@MANPAGE_NAME@ /port:12345
+
+When run as user within a X session (for example from an xterm) a socket on
+12345 is opened and the current display is shared via RDP.
+
+.SH EXIT STATUS
+.TP
+.B 0
+Successful program execution.
+.TP
+.B 1
+Otherwise.
+
+.SH SEE ALSO
+wlog(7)
+
+.SH AUTHOR
+FreeRDP <team@freerdp.com>
diff --git a/server/shadow/freerdp-shadow.pc.in b/server/shadow/freerdp-shadow.pc.in
new file mode 100644
index 0000000..805e2af
--- /dev/null
+++ b/server/shadow/freerdp-shadow.pc.in
@@ -0,0 +1,15 @@
+prefix=@PKG_CONFIG_INSTALL_PREFIX@
+exec_prefix=${prefix}
+libdir=${prefix}/@CMAKE_INSTALL_LIBDIR@
+includedir=${prefix}/@FREERDP_INCLUDE_DIR@
+libs=-lfreerdp-shadow@FREERDP_API_VERSION@ -lfreerdp-shadow-subsystem@FREERDP_API_VERSION@
+
+Name: FreeRDP shadow
+Description: FreeRDP: A Remote Desktop Protocol Implementation
+URL: http://www.freerdp.com/
+Version: @FREERDP_VERSION@
+Requires:
+Requires.private: @WINPR_PKG_CONFIG_FILENAME@ freerdp@FREERDP_API_VERSION@
+Libs: -L${libdir} ${libs}
+Libs.private: -ldl -lpthread
+Cflags: -I${includedir}
diff --git a/server/shadow/shadow.c b/server/shadow/shadow.c
new file mode 100644
index 0000000..14c9014
--- /dev/null
+++ b/server/shadow/shadow.c
@@ -0,0 +1,179 @@
+/**
+ * FreeRDP: A Remote Desktop Protocol Implementation
+ *
+ * Copyright 2014 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.
+ */
+
+#include <freerdp/config.h>
+
+#include <winpr/crt.h>
+#include <winpr/ssl.h>
+#include <winpr/path.h>
+#include <winpr/cmdline.h>
+#include <winpr/winsock.h>
+
+#include <winpr/tools/makecert.h>
+
+#include <freerdp/server/shadow.h>
+#include <freerdp/settings.h>
+
+#include <freerdp/log.h>
+#define TAG SERVER_TAG("shadow")
+
+int main(int argc, char** argv)
+{
+ int status = 0;
+ DWORD dwExitCode = 0;
+ COMMAND_LINE_ARGUMENT_A shadow_args[] = {
+ { "log-filters", COMMAND_LINE_VALUE_REQUIRED, "<tag>:<level>[,<tag>:<level>[,...]]", NULL,
+ NULL, -1, NULL, "Set logger filters, see wLog(7) for details" },
+ { "log-level", COMMAND_LINE_VALUE_REQUIRED, "[OFF|FATAL|ERROR|WARN|INFO|DEBUG|TRACE]", NULL,
+ NULL, -1, NULL, "Set the default log level, see wLog(7) for details" },
+ { "port", COMMAND_LINE_VALUE_REQUIRED, "<number>", NULL, NULL, -1, NULL, "Server port" },
+ { "ipc-socket", COMMAND_LINE_VALUE_REQUIRED, "<ipc-socket>", NULL, NULL, -1, NULL,
+ "Server IPC socket" },
+ { "bind-address", COMMAND_LINE_VALUE_REQUIRED, "<bind-address>[,<another address>, ...]",
+ NULL, NULL, -1, NULL,
+ "An address to bind to. Use '[<ipv6>]' for IPv6 addresses, e.g. '[::1]' for "
+ "localhost" },
+ { "monitors", COMMAND_LINE_VALUE_OPTIONAL, "<0,1,2...>", NULL, NULL, -1, NULL,
+ "Select or list monitors" },
+ { "max-connections", COMMAND_LINE_VALUE_REQUIRED, "<number>", 0, NULL, -1, NULL,
+ "maximum connections allowed to server, 0 to deactivate" },
+ { "rect", COMMAND_LINE_VALUE_REQUIRED, "<x,y,w,h>", NULL, NULL, -1, NULL,
+ "Select rectangle within monitor to share" },
+ { "auth", COMMAND_LINE_VALUE_BOOL, NULL, BoolValueFalse, NULL, -1, NULL,
+ "Clients must authenticate" },
+ { "remote-guard", COMMAND_LINE_VALUE_BOOL, NULL, BoolValueFalse, NULL, -1, NULL,
+ "Remote credential guard" },
+ { "may-view", COMMAND_LINE_VALUE_BOOL, NULL, BoolValueTrue, NULL, -1, NULL,
+ "Clients may view without prompt" },
+ { "may-interact", COMMAND_LINE_VALUE_BOOL, NULL, BoolValueTrue, NULL, -1, NULL,
+ "Clients may interact without prompt" },
+ { "sec", COMMAND_LINE_VALUE_REQUIRED, "<rdp|tls|nla|ext>", NULL, NULL, -1, NULL,
+ "force specific protocol security" },
+ { "sec-rdp", COMMAND_LINE_VALUE_BOOL, NULL, BoolValueTrue, NULL, -1, NULL,
+ "rdp protocol security" },
+ { "sec-tls", COMMAND_LINE_VALUE_BOOL, NULL, BoolValueTrue, NULL, -1, NULL,
+ "tls protocol security" },
+ { "sec-nla", COMMAND_LINE_VALUE_BOOL, NULL, BoolValueTrue, NULL, -1, NULL,
+ "nla protocol security" },
+ { "sec-ext", COMMAND_LINE_VALUE_BOOL, NULL, BoolValueFalse, NULL, -1, NULL,
+ "nla extended protocol security" },
+ { "sam-file", COMMAND_LINE_VALUE_REQUIRED, "<file>", NULL, NULL, -1, NULL,
+ "NTLM SAM file for NLA authentication" },
+ { "keytab", COMMAND_LINE_VALUE_REQUIRED, "<file>", NULL, NULL, -1, NULL,
+ "Kerberos keytab file for NLA authentication" },
+ { "ccache", COMMAND_LINE_VALUE_REQUIRED, "<file>", NULL, NULL, -1, NULL,
+ "Kerberos host ccache file for NLA authentication" },
+ { "tls-secrets-file", COMMAND_LINE_VALUE_REQUIRED, "<file>", NULL, NULL, -1, NULL,
+ "file where tls secrets shall be stored" },
+ { "gfx-progressive", COMMAND_LINE_VALUE_BOOL, NULL, BoolValueTrue, NULL, -1, NULL,
+ "Allow GFX progressive codec" },
+ { "gfx-rfx", COMMAND_LINE_VALUE_BOOL, NULL, BoolValueTrue, NULL, -1, NULL,
+ "Allow GFX RFX codec" },
+ { "gfx-planar", COMMAND_LINE_VALUE_BOOL, NULL, BoolValueTrue, NULL, -1, NULL,
+ "Allow GFX planar codec" },
+ { "gfx-avc420", COMMAND_LINE_VALUE_BOOL, NULL, BoolValueTrue, NULL, -1, NULL,
+ "Allow GFX AVC420 codec" },
+ { "gfx-avc444", COMMAND_LINE_VALUE_BOOL, NULL, BoolValueTrue, NULL, -1, NULL,
+ "Allow GFX AVC444 codec" },
+ { "version", COMMAND_LINE_VALUE_FLAG | COMMAND_LINE_PRINT_VERSION, NULL, NULL, NULL, -1,
+ NULL, "Print version" },
+ { "buildconfig", COMMAND_LINE_VALUE_FLAG | COMMAND_LINE_PRINT_BUILDCONFIG, NULL, NULL, NULL,
+ -1, NULL, "Print the build configuration" },
+ { "help", COMMAND_LINE_VALUE_FLAG | COMMAND_LINE_PRINT_HELP, NULL, NULL, NULL, -1, "?",
+ "Print help" },
+ { NULL, 0, NULL, NULL, NULL, -1, NULL, NULL }
+ };
+
+ shadow_subsystem_set_entry_builtin(NULL);
+
+ rdpShadowServer* server = shadow_server_new();
+
+ if (!server)
+ {
+ status = -1;
+ WLog_ERR(TAG, "Server new failed");
+ goto fail;
+ }
+
+ rdpSettings* settings = server->settings;
+ WINPR_ASSERT(settings);
+
+ if (!freerdp_settings_set_bool(settings, FreeRDP_NlaSecurity, TRUE) ||
+ !freerdp_settings_set_bool(settings, FreeRDP_TlsSecurity, TRUE) ||
+ !freerdp_settings_set_bool(settings, FreeRDP_RdpSecurity, TRUE))
+ goto fail;
+
+ /* By default allow all GFX modes.
+ * This can be changed with command line flags [+|-]gfx-CODEC
+ */
+ if (!freerdp_settings_set_uint32(settings, FreeRDP_ColorDepth, 32) ||
+ !freerdp_settings_set_bool(settings, FreeRDP_NSCodec, TRUE) ||
+ !freerdp_settings_set_bool(settings, FreeRDP_RemoteFxCodec, TRUE) ||
+ !freerdp_settings_set_bool(settings, FreeRDP_GfxH264, TRUE) ||
+ !freerdp_settings_set_bool(settings, FreeRDP_GfxAVC444, TRUE) ||
+ !freerdp_settings_set_bool(settings, FreeRDP_GfxAVC444v2, TRUE) ||
+ !freerdp_settings_set_bool(settings, FreeRDP_GfxProgressive, TRUE) ||
+ !freerdp_settings_set_bool(settings, FreeRDP_GfxProgressiveV2, TRUE))
+ goto fail;
+
+ /* TODO: We do not implement relative mouse callbacks, so deactivate it for now */
+ if (!freerdp_settings_set_bool(settings, FreeRDP_MouseUseRelativeMove, FALSE) ||
+ !freerdp_settings_set_bool(settings, FreeRDP_HasRelativeMouseEvent, FALSE))
+ goto fail;
+
+ if ((status = shadow_server_parse_command_line(server, argc, argv, shadow_args)) < 0)
+ {
+ shadow_server_command_line_status_print(server, argc, argv, status, shadow_args);
+ goto fail;
+ }
+
+ if ((status = shadow_server_init(server)) < 0)
+ {
+ WLog_ERR(TAG, "Server initialization failed.");
+ goto fail;
+ }
+
+ if ((status = shadow_server_start(server)) < 0)
+ {
+ WLog_ERR(TAG, "Failed to start server.");
+ goto fail;
+ }
+
+#ifdef _WIN32
+ {
+ MSG msg = { 0 };
+ while (GetMessage(&msg, 0, 0, 0))
+ {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+ }
+#endif
+
+ WaitForSingleObject(server->thread, INFINITE);
+
+ if (!GetExitCodeThread(server->thread, &dwExitCode))
+ status = -1;
+ else
+ status = (int)dwExitCode;
+
+fail:
+ shadow_server_uninit(server);
+ shadow_server_free(server);
+ return status;
+}
diff --git a/server/shadow/shadow.h b/server/shadow/shadow.h
new file mode 100644
index 0000000..33eb805
--- /dev/null
+++ b/server/shadow/shadow.h
@@ -0,0 +1,44 @@
+/**
+ * FreeRDP: A Remote Desktop Protocol Implementation
+ *
+ * Copyright 2014 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.
+ */
+
+#ifndef FREERDP_SERVER_SHADOW_SHADOW_H
+#define FREERDP_SERVER_SHADOW_SHADOW_H
+
+#include <freerdp/server/shadow.h>
+
+#include "shadow_client.h"
+#include "shadow_input.h"
+#include "shadow_screen.h"
+#include "shadow_surface.h"
+#include "shadow_encoder.h"
+#include "shadow_capture.h"
+#include "shadow_channels.h"
+#include "shadow_subsystem.h"
+#include "shadow_lobby.h"
+#include "shadow_mcevent.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* FREERDP_SERVER_SHADOW_SHADOW_H */
diff --git a/server/shadow/shadow_audin.c b/server/shadow/shadow_audin.c
new file mode 100644
index 0000000..9e7f6ef
--- /dev/null
+++ b/server/shadow/shadow_audin.c
@@ -0,0 +1,104 @@
+/**
+ * FreeRDP: A Remote Desktop Protocol Implementation
+ *
+ * Copyright 2015 Jiang Zihao <zihao.jiang@yahoo.com>
+ * Copyright 2023 Pascal Nowack <Pascal.Nowack@gmx.de>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <freerdp/config.h>
+
+#include <freerdp/log.h>
+#include "shadow.h"
+
+#include "shadow_audin.h"
+#include <freerdp/server/server-common.h>
+
+#if defined(CHANNEL_AUDIN_SERVER)
+#include <freerdp/server/audin.h>
+#endif
+
+#define TAG SERVER_TAG("shadow")
+
+#if defined(CHANNEL_AUDIN_SERVER)
+
+static UINT AudinServerData(audin_server_context* audin, const SNDIN_DATA* data)
+{
+ rdpShadowClient* client = NULL;
+ rdpShadowSubsystem* subsystem = NULL;
+
+ WINPR_ASSERT(audin);
+ WINPR_ASSERT(data);
+
+ client = audin->userdata;
+ WINPR_ASSERT(client);
+ WINPR_ASSERT(client->server);
+ subsystem = client->server->subsystem;
+ WINPR_ASSERT(subsystem);
+
+ if (!client->mayInteract)
+ return CHANNEL_RC_OK;
+
+ if (!IFCALLRESULT(TRUE, subsystem->AudinServerReceiveSamples, subsystem, client,
+ audin_server_get_negotiated_format(client->audin), data->Data))
+ return ERROR_INTERNAL_ERROR;
+
+ return CHANNEL_RC_OK;
+}
+
+#endif
+
+BOOL shadow_client_audin_init(rdpShadowClient* client)
+{
+ WINPR_ASSERT(client);
+
+#if defined(CHANNEL_AUDIN_SERVER)
+ audin_server_context* audin = client->audin = audin_server_context_new(client->vcm);
+
+ if (!audin)
+ return FALSE;
+
+ audin->userdata = client;
+
+ audin->Data = AudinServerData;
+
+ if (client->subsystem->audinFormats)
+ {
+ if (!audin_server_set_formats(client->audin, client->subsystem->nAudinFormats,
+ client->subsystem->audinFormats))
+ goto fail;
+ }
+ else
+ {
+ if (!audin_server_set_formats(client->audin, -1, NULL))
+ goto fail;
+ }
+
+ return TRUE;
+fail:
+ audin_server_context_free(audin);
+ client->audin = NULL;
+#endif
+ return FALSE;
+}
+
+void shadow_client_audin_uninit(rdpShadowClient* client)
+{
+ WINPR_ASSERT(client);
+
+#if defined(CHANNEL_AUDIN_SERVER)
+ audin_server_context_free(client->audin);
+ client->audin = NULL;
+#endif
+}
diff --git a/server/shadow/shadow_audin.h b/server/shadow/shadow_audin.h
new file mode 100644
index 0000000..0499987
--- /dev/null
+++ b/server/shadow/shadow_audin.h
@@ -0,0 +1,39 @@
+/**
+ * FreeRDP: A Remote Desktop Protocol Implementation
+ *
+ * Copyright 2015 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_SERVER_SHADOW_AUDIN_H
+#define FREERDP_SERVER_SHADOW_AUDIN_H
+
+#include <freerdp/server/shadow.h>
+
+#include <winpr/crt.h>
+#include <winpr/synch.h>
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+ BOOL shadow_client_audin_init(rdpShadowClient* client);
+ void shadow_client_audin_uninit(rdpShadowClient* client);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* FREERDP_SERVER_SHADOW_AUDIN_H */
diff --git a/server/shadow/shadow_capture.c b/server/shadow/shadow_capture.c
new file mode 100644
index 0000000..4711ded
--- /dev/null
+++ b/server/shadow/shadow_capture.c
@@ -0,0 +1,263 @@
+/**
+ * FreeRDP: A Remote Desktop Protocol Implementation
+ *
+ * Copyright 2014 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.
+ */
+
+#include <freerdp/config.h>
+
+#include <winpr/crt.h>
+#include <winpr/print.h>
+
+#include <freerdp/log.h>
+
+#include "shadow_surface.h"
+
+#include "shadow_capture.h"
+
+#define TAG SERVER_TAG("shadow")
+
+int shadow_capture_align_clip_rect(RECTANGLE_16* rect, RECTANGLE_16* clip)
+{
+ int dx = 0;
+ int dy = 0;
+ dx = (rect->left % 16);
+
+ if (dx != 0)
+ {
+ rect->left -= dx;
+ rect->right += dx;
+ }
+
+ dx = (rect->right % 16);
+
+ if (dx != 0)
+ {
+ rect->right += (16 - dx);
+ }
+
+ dy = (rect->top % 16);
+
+ if (dy != 0)
+ {
+ rect->top -= dy;
+ rect->bottom += dy;
+ }
+
+ dy = (rect->bottom % 16);
+
+ if (dy != 0)
+ {
+ rect->bottom += (16 - dy);
+ }
+
+ if (rect->left < clip->left)
+ rect->left = clip->left;
+
+ if (rect->top < clip->top)
+ rect->top = clip->top;
+
+ if (rect->right > clip->right)
+ rect->right = clip->right;
+
+ if (rect->bottom > clip->bottom)
+ rect->bottom = clip->bottom;
+
+ return 1;
+}
+
+int shadow_capture_compare(BYTE* pData1, UINT32 nStep1, UINT32 nWidth, UINT32 nHeight, BYTE* pData2,
+ UINT32 nStep2, RECTANGLE_16* rect)
+{
+ BOOL equal = 0;
+ BOOL allEqual = 0;
+ UINT32 tw = 0;
+ UINT32 th = 0;
+ UINT32 nrow = 0;
+ UINT32 ncol = 0;
+ UINT32 l = 0;
+ UINT32 t = 0;
+ UINT32 r = 0;
+ UINT32 b = 0;
+ BYTE* p1 = NULL;
+ BYTE* p2 = NULL;
+ BOOL rows[1024];
+#ifdef WITH_DEBUG_SHADOW_CAPTURE
+ BOOL cols[1024] = { FALSE };
+#endif
+ allEqual = TRUE;
+ ZeroMemory(rect, sizeof(RECTANGLE_16));
+ FillMemory(rows, sizeof(rows), 0xFF);
+#ifdef WITH_DEBUG_SHADOW_CAPTURE
+ FillMemory(cols, sizeof(cols), 0xFF);
+#endif
+ nrow = (nHeight + 15) / 16;
+ ncol = (nWidth + 15) / 16;
+ l = ncol + 1;
+ r = 0;
+ t = nrow + 1;
+ b = 0;
+
+ for (UINT32 ty = 0; ty < nrow; ty++)
+ {
+ th = ((ty + 1) == nrow) ? (nHeight % 16) : 16;
+
+ if (!th)
+ th = 16;
+
+ for (UINT32 tx = 0; tx < ncol; tx++)
+ {
+ equal = TRUE;
+ tw = ((tx + 1) == ncol) ? (nWidth % 16) : 16;
+
+ if (!tw)
+ tw = 16;
+
+ p1 = &pData1[(ty * 16 * nStep1) + (tx * 16 * 4)];
+ p2 = &pData2[(ty * 16 * nStep2) + (tx * 16 * 4)];
+
+ for (UINT32 k = 0; k < th; k++)
+ {
+ if (memcmp(p1, p2, tw * 4) != 0)
+ {
+ equal = FALSE;
+ break;
+ }
+
+ p1 += nStep1;
+ p2 += nStep2;
+ }
+
+ if (!equal)
+ {
+ rows[ty] = FALSE;
+#ifdef WITH_DEBUG_SHADOW_CAPTURE
+ cols[tx] = FALSE;
+#endif
+
+ if (l > tx)
+ l = tx;
+
+ if (r < tx)
+ r = tx;
+ }
+ }
+
+ if (!rows[ty])
+ {
+ allEqual = FALSE;
+
+ if (t > ty)
+ t = ty;
+
+ if (b < ty)
+ b = ty;
+ }
+ }
+
+ if (allEqual)
+ return 0;
+
+ WINPR_ASSERT(l * 16 <= UINT16_MAX);
+ WINPR_ASSERT(t * 16 <= UINT16_MAX);
+ WINPR_ASSERT((r + 1) * 16 <= UINT16_MAX);
+ WINPR_ASSERT((b + 1) * 16 <= UINT16_MAX);
+ rect->left = (UINT16)l * 16;
+ rect->top = (UINT16)t * 16;
+ rect->right = (UINT16)(r + 1) * 16;
+ rect->bottom = (UINT16)(b + 1) * 16;
+
+ WINPR_ASSERT(nWidth <= UINT16_MAX);
+ if (rect->right > nWidth)
+ rect->right = (UINT16)nWidth;
+
+ WINPR_ASSERT(nHeight <= UINT16_MAX);
+ if (rect->bottom > nHeight)
+ rect->bottom = (UINT16)nHeight;
+
+#ifdef WITH_DEBUG_SHADOW_CAPTURE
+ size_t size = ncol + 1;
+ char* col_str = calloc(size, sizeof(char));
+
+ if (!col_str)
+ {
+ WLog_ERR(TAG, "calloc failed!");
+ return 1;
+ }
+
+ for (UINT32 tx = 0; tx < ncol; tx++)
+ sprintf_s(&col_str[tx], size - tx, "-");
+
+ WLog_INFO(TAG, "%s", col_str);
+
+ for (UINT32 tx = 0; tx < ncol; tx++)
+ sprintf_s(&col_str[tx], size - tx, "%c", cols[tx] ? 'O' : 'X');
+
+ WLog_INFO(TAG, "%s", col_str);
+
+ for (UINT32 tx = 0; tx < ncol; tx++)
+ sprintf_s(&col_str[tx], size - tx, "-");
+
+ WLog_INFO(TAG, "%s", col_str);
+
+ for (UINT32 ty = 0; ty < nrow; ty++)
+ {
+ for (UINT32 tx = 0; tx < ncol; tx++)
+ sprintf_s(&col_str[tx], size - tx, "%c", cols[tx] ? 'O' : 'X');
+
+ WLog_INFO(TAG, "%s", col_str);
+ WLog_INFO(TAG, "|%s|", rows[ty] ? "O" : "X");
+ }
+
+ WLog_INFO(TAG,
+ "left: %" PRIu32 " top: %" PRIu32 " right: %" PRIu32 " bottom: %" PRIu32
+ " ncol: %" PRIu32 " nrow: %" PRIu32,
+ l, t, r, b, ncol, nrow);
+ free(col_str);
+#endif
+ return 1;
+}
+
+rdpShadowCapture* shadow_capture_new(rdpShadowServer* server)
+{
+ WINPR_ASSERT(server);
+
+ rdpShadowCapture* capture = (rdpShadowCapture*)calloc(1, sizeof(rdpShadowCapture));
+
+ if (!capture)
+ return NULL;
+
+ capture->server = server;
+
+ if (!InitializeCriticalSectionAndSpinCount(&(capture->lock), 4000))
+ {
+ WINPR_PRAGMA_DIAG_PUSH
+ WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
+ shadow_capture_free(capture);
+ WINPR_PRAGMA_DIAG_POP
+ return NULL;
+ }
+
+ return capture;
+}
+
+void shadow_capture_free(rdpShadowCapture* capture)
+{
+ if (!capture)
+ return;
+
+ DeleteCriticalSection(&(capture->lock));
+ free(capture);
+}
diff --git a/server/shadow/shadow_capture.h b/server/shadow/shadow_capture.h
new file mode 100644
index 0000000..31de550
--- /dev/null
+++ b/server/shadow/shadow_capture.h
@@ -0,0 +1,52 @@
+/**
+ * FreeRDP: A Remote Desktop Protocol Implementation
+ *
+ * Copyright 2014 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.
+ */
+
+#ifndef FREERDP_SERVER_SHADOW_CAPTURE_H
+#define FREERDP_SERVER_SHADOW_CAPTURE_H
+
+#include <freerdp/server/shadow.h>
+
+#include <winpr/crt.h>
+#include <winpr/winpr.h>
+#include <winpr/synch.h>
+
+struct rdp_shadow_capture
+{
+ rdpShadowServer* server;
+
+ int width;
+ int height;
+
+ CRITICAL_SECTION lock;
+};
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+ void shadow_capture_free(rdpShadowCapture* capture);
+
+ WINPR_ATTR_MALLOC(shadow_capture_free, 1)
+ rdpShadowCapture* shadow_capture_new(rdpShadowServer* server);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* FREERDP_SERVER_SHADOW_CAPTURE_H */
diff --git a/server/shadow/shadow_channels.c b/server/shadow/shadow_channels.c
new file mode 100644
index 0000000..07c5105
--- /dev/null
+++ b/server/shadow/shadow_channels.c
@@ -0,0 +1,66 @@
+/**
+ * FreeRDP: A Remote Desktop Protocol Implementation
+ *
+ * Copyright 2014 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.
+ */
+
+#include <freerdp/config.h>
+
+#include "shadow.h"
+
+#include "shadow_channels.h"
+
+UINT shadow_client_channels_post_connect(rdpShadowClient* client)
+{
+ if (WTSVirtualChannelManagerIsChannelJoined(client->vcm, ENCOMSP_SVC_CHANNEL_NAME))
+ {
+ shadow_client_encomsp_init(client);
+ }
+
+ if (WTSVirtualChannelManagerIsChannelJoined(client->vcm, REMDESK_SVC_CHANNEL_NAME))
+ {
+ shadow_client_remdesk_init(client);
+ }
+
+ if (WTSVirtualChannelManagerIsChannelJoined(client->vcm, RDPSND_CHANNEL_NAME))
+ {
+ shadow_client_rdpsnd_init(client);
+ }
+
+ shadow_client_audin_init(client);
+
+ if (freerdp_settings_get_bool(client->context.settings, FreeRDP_SupportGraphicsPipeline))
+ {
+ shadow_client_rdpgfx_init(client);
+ }
+
+ return CHANNEL_RC_OK;
+}
+
+void shadow_client_channels_free(rdpShadowClient* client)
+{
+ if (freerdp_settings_get_bool(client->context.settings, FreeRDP_SupportGraphicsPipeline))
+ {
+ shadow_client_rdpgfx_uninit(client);
+ }
+
+ shadow_client_audin_uninit(client);
+
+ shadow_client_rdpsnd_uninit(client);
+
+ shadow_client_remdesk_uninit(client);
+
+ shadow_client_encomsp_uninit(client);
+}
diff --git a/server/shadow/shadow_channels.h b/server/shadow/shadow_channels.h
new file mode 100644
index 0000000..261041e
--- /dev/null
+++ b/server/shadow/shadow_channels.h
@@ -0,0 +1,45 @@
+/**
+ * FreeRDP: A Remote Desktop Protocol Implementation
+ *
+ * Copyright 2014 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.
+ */
+
+#ifndef FREERDP_SERVER_SHADOW_CHANNELS_H
+#define FREERDP_SERVER_SHADOW_CHANNELS_H
+
+#include <freerdp/server/shadow.h>
+
+#include <winpr/crt.h>
+#include <winpr/synch.h>
+
+#include "shadow_encomsp.h"
+#include "shadow_remdesk.h"
+#include "shadow_rdpsnd.h"
+#include "shadow_audin.h"
+#include "shadow_rdpgfx.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+ UINT shadow_client_channels_post_connect(rdpShadowClient* client);
+ void shadow_client_channels_free(rdpShadowClient* client);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* FREERDP_SERVER_SHADOW_CHANNELS_H */
diff --git a/server/shadow/shadow_client.c b/server/shadow/shadow_client.c
new file mode 100644
index 0000000..0fd5236
--- /dev/null
+++ b/server/shadow/shadow_client.c
@@ -0,0 +1,2612 @@
+/**
+ * FreeRDP: A Remote Desktop Protocol Implementation
+ *
+ * Copyright 2014 Marc-Andre Moreau <marcandre.moreau@gmail.com>
+ * Copyright 2017 Armin Novak <armin.novak@thincast.com>
+ * Copyright 2017 Thincast Technologies GmbH
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <freerdp/config.h>
+
+#include <winpr/crt.h>
+#include <winpr/assert.h>
+#include <winpr/file.h>
+#include <winpr/path.h>
+#include <winpr/synch.h>
+#include <winpr/thread.h>
+#include <winpr/sysinfo.h>
+#include <winpr/interlocked.h>
+
+#include <freerdp/log.h>
+#include <freerdp/channels/drdynvc.h>
+
+#include "shadow.h"
+
+#define TAG CLIENT_TAG("shadow")
+
+typedef struct
+{
+ BOOL gfxOpened;
+ BOOL gfxSurfaceCreated;
+} SHADOW_GFX_STATUS;
+
+static INLINE BOOL shadow_client_rdpgfx_new_surface(rdpShadowClient* client)
+{
+ UINT error = CHANNEL_RC_OK;
+ RDPGFX_CREATE_SURFACE_PDU createSurface;
+ RDPGFX_MAP_SURFACE_TO_OUTPUT_PDU surfaceToOutput;
+ RdpgfxServerContext* context = NULL;
+ rdpSettings* settings = NULL;
+
+ WINPR_ASSERT(client);
+ context = client->rdpgfx;
+ WINPR_ASSERT(context);
+ settings = ((rdpContext*)client)->settings;
+ WINPR_ASSERT(settings);
+
+ WINPR_ASSERT(freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth) <= UINT16_MAX);
+ WINPR_ASSERT(freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight) <= UINT16_MAX);
+ createSurface.width = (UINT16)freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth);
+ createSurface.height = (UINT16)freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight);
+ createSurface.pixelFormat = GFX_PIXEL_FORMAT_XRGB_8888;
+ createSurface.surfaceId = client->surfaceId;
+ surfaceToOutput.outputOriginX = 0;
+ surfaceToOutput.outputOriginY = 0;
+ surfaceToOutput.surfaceId = client->surfaceId;
+ surfaceToOutput.reserved = 0;
+ IFCALLRET(context->CreateSurface, error, context, &createSurface);
+
+ if (error)
+ {
+ WLog_ERR(TAG, "CreateSurface failed with error %" PRIu32 "", error);
+ return FALSE;
+ }
+
+ IFCALLRET(context->MapSurfaceToOutput, error, context, &surfaceToOutput);
+
+ if (error)
+ {
+ WLog_ERR(TAG, "MapSurfaceToOutput failed with error %" PRIu32 "", error);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static INLINE BOOL shadow_client_rdpgfx_release_surface(rdpShadowClient* client)
+{
+ UINT error = CHANNEL_RC_OK;
+ RDPGFX_DELETE_SURFACE_PDU pdu;
+ RdpgfxServerContext* context = NULL;
+
+ WINPR_ASSERT(client);
+
+ context = client->rdpgfx;
+ WINPR_ASSERT(context);
+
+ pdu.surfaceId = client->surfaceId++;
+ IFCALLRET(context->DeleteSurface, error, context, &pdu);
+
+ if (error)
+ {
+ WLog_ERR(TAG, "DeleteSurface failed with error %" PRIu32 "", error);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static INLINE BOOL shadow_client_rdpgfx_reset_graphic(rdpShadowClient* client)
+{
+ UINT error = CHANNEL_RC_OK;
+ RDPGFX_RESET_GRAPHICS_PDU pdu = { 0 };
+ RdpgfxServerContext* context = NULL;
+ rdpSettings* settings = NULL;
+
+ WINPR_ASSERT(client);
+ WINPR_ASSERT(client->rdpgfx);
+
+ context = client->rdpgfx;
+ WINPR_ASSERT(context);
+
+ settings = client->context.settings;
+ WINPR_ASSERT(settings);
+
+ pdu.width = freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth);
+ pdu.height = freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight);
+ pdu.monitorCount = client->subsystem->numMonitors;
+ pdu.monitorDefArray = client->subsystem->monitors;
+ IFCALLRET(context->ResetGraphics, error, context, &pdu);
+
+ if (error)
+ {
+ WLog_ERR(TAG, "ResetGraphics failed with error %" PRIu32 "", error);
+ return FALSE;
+ }
+
+ client->first_frame = TRUE;
+ return TRUE;
+}
+
+static INLINE void shadow_client_free_queued_message(void* obj)
+{
+ wMessage* message = (wMessage*)obj;
+
+ WINPR_ASSERT(message);
+ if (message->Free)
+ {
+ message->Free(message);
+ message->Free = NULL;
+ }
+}
+
+static void shadow_client_context_free(freerdp_peer* peer, rdpContext* context)
+{
+ rdpShadowClient* client = (rdpShadowClient*)context;
+ rdpShadowServer* server = NULL;
+
+ WINPR_UNUSED(peer);
+ if (!client)
+ return;
+
+ server = client->server;
+ if (server && server->clients)
+ ArrayList_Remove(server->clients, (void*)client);
+
+ shadow_encoder_free(client->encoder);
+
+ /* Clear queued messages and free resource */
+ MessageQueue_Free(client->MsgQueue);
+ WTSCloseServer((HANDLE)client->vcm);
+ region16_uninit(&(client->invalidRegion));
+ DeleteCriticalSection(&(client->lock));
+
+ client->MsgQueue = NULL;
+ client->encoder = NULL;
+ client->vcm = NULL;
+}
+
+static BOOL shadow_client_context_new(freerdp_peer* peer, rdpContext* context)
+{
+ BOOL NSCodec = 0;
+ const char bind_address[] = "bind-address,";
+ rdpShadowClient* client = (rdpShadowClient*)context;
+ rdpSettings* settings = NULL;
+ const rdpSettings* srvSettings = NULL;
+ rdpShadowServer* server = NULL;
+ const wObject cb = { NULL, NULL, NULL, shadow_client_free_queued_message, NULL };
+
+ WINPR_ASSERT(client);
+ WINPR_ASSERT(peer);
+ WINPR_ASSERT(peer->context);
+
+ server = (rdpShadowServer*)peer->ContextExtra;
+ WINPR_ASSERT(server);
+
+ srvSettings = server->settings;
+ WINPR_ASSERT(srvSettings);
+
+ client->surfaceId = 1;
+ client->server = server;
+ client->subsystem = server->subsystem;
+ WINPR_ASSERT(client->subsystem);
+
+ settings = peer->context->settings;
+ WINPR_ASSERT(settings);
+
+ if (!freerdp_settings_set_uint32(settings, FreeRDP_ColorDepth,
+ freerdp_settings_get_uint32(srvSettings, FreeRDP_ColorDepth)))
+ return FALSE;
+ NSCodec = freerdp_settings_get_bool(srvSettings, FreeRDP_NSCodec);
+ if (!freerdp_settings_set_bool(settings, FreeRDP_NSCodec, NSCodec))
+ return FALSE;
+ if (!freerdp_settings_set_bool(settings, FreeRDP_RemoteFxCodec,
+ freerdp_settings_get_bool(srvSettings, FreeRDP_RemoteFxCodec)))
+ return FALSE;
+ if (!freerdp_settings_set_bool(settings, FreeRDP_BitmapCacheV3Enabled, TRUE))
+ return FALSE;
+ if (!freerdp_settings_set_bool(settings, FreeRDP_FrameMarkerCommandEnabled, TRUE))
+ return FALSE;
+ if (!freerdp_settings_set_bool(settings, FreeRDP_SurfaceFrameMarkerEnabled, TRUE))
+ return FALSE;
+ if (!freerdp_settings_set_bool(settings, FreeRDP_SupportGraphicsPipeline, TRUE))
+ return FALSE;
+ if (!freerdp_settings_set_bool(settings, FreeRDP_GfxH264,
+ freerdp_settings_get_bool(srvSettings, FreeRDP_GfxH264)))
+ return FALSE;
+ if (!freerdp_settings_set_bool(settings, FreeRDP_DrawAllowSkipAlpha, TRUE))
+ return FALSE;
+ if (!freerdp_settings_set_bool(settings, FreeRDP_DrawAllowColorSubsampling, TRUE))
+ return FALSE;
+ if (!freerdp_settings_set_bool(settings, FreeRDP_DrawAllowDynamicColorFidelity, TRUE))
+ return FALSE;
+ if (!freerdp_settings_set_uint32(settings, FreeRDP_CompressionLevel, PACKET_COMPR_TYPE_RDP8))
+ return FALSE;
+
+ if (server->ipcSocket && (strncmp(bind_address, server->ipcSocket,
+ strnlen(bind_address, sizeof(bind_address))) != 0))
+ {
+ if (!freerdp_settings_set_bool(settings, FreeRDP_LyncRdpMode, TRUE))
+ return FALSE;
+ if (!freerdp_settings_set_bool(settings, FreeRDP_CompressionEnabled, FALSE))
+ return FALSE;
+ }
+
+ client->inLobby = TRUE;
+ client->mayView = server->mayView;
+ client->mayInteract = server->mayInteract;
+
+ if (!InitializeCriticalSectionAndSpinCount(&(client->lock), 4000))
+ goto fail;
+
+ region16_init(&(client->invalidRegion));
+ client->vcm = WTSOpenServerA(peer->context);
+
+ if (!client->vcm || client->vcm == INVALID_HANDLE_VALUE)
+ goto fail;
+
+ if (!(client->MsgQueue = MessageQueue_New(&cb)))
+ goto fail;
+
+ if (!(client->encoder = shadow_encoder_new(client)))
+ goto fail;
+
+ if (!ArrayList_Append(server->clients, (void*)client))
+ goto fail;
+
+ return TRUE;
+
+fail:
+ shadow_client_context_free(peer, context);
+ return FALSE;
+}
+
+static INLINE void shadow_client_mark_invalid(rdpShadowClient* client, UINT32 numRects,
+ const RECTANGLE_16* rects)
+{
+ RECTANGLE_16 screenRegion;
+ rdpSettings* settings = NULL;
+
+ WINPR_ASSERT(client);
+ WINPR_ASSERT(rects || (numRects == 0));
+
+ settings = client->context.settings;
+ WINPR_ASSERT(settings);
+
+ EnterCriticalSection(&(client->lock));
+
+ /* Mark client invalid region. No rectangle means full screen */
+ if (numRects > 0)
+ {
+ for (UINT32 index = 0; index < numRects; index++)
+ {
+ region16_union_rect(&(client->invalidRegion), &(client->invalidRegion), &rects[index]);
+ }
+ }
+ else
+ {
+ screenRegion.left = 0;
+ screenRegion.top = 0;
+ WINPR_ASSERT(freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth) <= UINT16_MAX);
+ WINPR_ASSERT(freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight) <= UINT16_MAX);
+ screenRegion.right = (UINT16)freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth);
+ screenRegion.bottom = (UINT16)freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight);
+ region16_union_rect(&(client->invalidRegion), &(client->invalidRegion), &screenRegion);
+ }
+
+ LeaveCriticalSection(&(client->lock));
+}
+
+/**
+ * Function description
+ * Recalculate client desktop size and update to rdpSettings
+ *
+ * @return TRUE if width/height changed.
+ */
+static INLINE BOOL shadow_client_recalc_desktop_size(rdpShadowClient* client)
+{
+ INT32 width = 0;
+ INT32 height = 0;
+ rdpShadowServer* server = NULL;
+ rdpSettings* settings = NULL;
+ RECTANGLE_16 viewport = { 0 };
+
+ WINPR_ASSERT(client);
+ server = client->server;
+ settings = client->context.settings;
+
+ WINPR_ASSERT(server);
+ WINPR_ASSERT(server->surface);
+ WINPR_ASSERT(settings);
+
+ WINPR_ASSERT(server->surface->width <= UINT16_MAX);
+ WINPR_ASSERT(server->surface->height <= UINT16_MAX);
+ viewport.right = (UINT16)server->surface->width;
+ viewport.bottom = (UINT16)server->surface->height;
+
+ if (server->shareSubRect)
+ {
+ rectangles_intersection(&viewport, &(server->subRect), &viewport);
+ }
+
+ width = viewport.right - viewport.left;
+ height = viewport.bottom - viewport.top;
+
+ WINPR_ASSERT(width >= 0);
+ WINPR_ASSERT(width <= UINT16_MAX);
+ WINPR_ASSERT(height >= 0);
+ WINPR_ASSERT(height <= UINT16_MAX);
+ if (freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth) != (UINT32)width ||
+ freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight) != (UINT32)height)
+ return TRUE;
+
+ return FALSE;
+}
+
+static BOOL shadow_client_capabilities(freerdp_peer* peer)
+{
+ rdpShadowSubsystem* subsystem = NULL;
+ rdpShadowClient* client = NULL;
+ BOOL ret = TRUE;
+
+ WINPR_ASSERT(peer);
+
+ client = (rdpShadowClient*)peer->context;
+ WINPR_ASSERT(client);
+ WINPR_ASSERT(client->server);
+
+ subsystem = client->server->subsystem;
+ WINPR_ASSERT(subsystem);
+
+ IFCALLRET(subsystem->ClientCapabilities, ret, subsystem, client);
+
+ if (!ret)
+ WLog_WARN(TAG, "subsystem->ClientCapabilities failed");
+
+ return ret;
+}
+
+static void shadow_reset_desktop_resize(rdpShadowClient* client)
+{
+ WINPR_ASSERT(client);
+ client->resizeRequested = FALSE;
+}
+
+static BOOL shadow_send_desktop_resize(rdpShadowClient* client)
+{
+ BOOL rc = 0;
+ rdpUpdate* update = NULL;
+ rdpSettings* settings = NULL;
+ const freerdp_peer* peer = NULL;
+
+ WINPR_ASSERT(client);
+
+ settings = client->context.settings;
+ peer = client->context.peer;
+ WINPR_ASSERT(peer);
+ WINPR_ASSERT(client->server);
+ WINPR_ASSERT(client->server->surface);
+
+ const UINT32 resizeWidth = client->server->surface->width;
+ const UINT32 resizeHeight = client->server->surface->height;
+
+ if (client->resizeRequested)
+ {
+ if ((resizeWidth == client->resizeWidth) && (resizeHeight == client->resizeHeight))
+ {
+ const UINT32 w = freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth);
+ const UINT32 h = freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight);
+ WLog_WARN(TAG,
+ "detected previous resize request for resolution %" PRIu32 "x%" PRIu32
+ ", still have %" PRIu32 "x%" PRIu32 ", disconnecting peer",
+ resizeWidth, resizeHeight, w, h);
+ return FALSE;
+ }
+ }
+
+ update = client->context.update;
+ WINPR_ASSERT(update);
+ WINPR_ASSERT(update->DesktopResize);
+
+ // Update peer resolution, required so that during disconnect/reconnect the correct resolution
+ // is sent to the client.
+ if (!freerdp_settings_set_uint32(settings, FreeRDP_DesktopWidth, resizeWidth))
+ return FALSE;
+ if (!freerdp_settings_set_uint32(settings, FreeRDP_DesktopHeight, resizeHeight))
+ return FALSE;
+ rc = update->DesktopResize(update->context);
+ WLog_INFO(TAG, "Client %s resize requested (%" PRIu32 "x%" PRIu32 "@%" PRIu32 ")",
+ peer->hostname, resizeWidth, resizeHeight,
+ freerdp_settings_get_uint32(settings, FreeRDP_ColorDepth));
+ client->resizeRequested = TRUE;
+ client->resizeWidth = resizeWidth;
+ client->resizeHeight = resizeHeight;
+
+ return rc;
+}
+
+static BOOL shadow_client_post_connect(freerdp_peer* peer)
+{
+ int authStatus = 0;
+ rdpSettings* settings = NULL;
+ rdpShadowClient* client = NULL;
+ rdpShadowServer* server = NULL;
+ rdpShadowSubsystem* subsystem = NULL;
+
+ WINPR_ASSERT(peer);
+
+ client = (rdpShadowClient*)peer->context;
+ WINPR_ASSERT(client);
+
+ settings = peer->context->settings;
+ WINPR_ASSERT(settings);
+
+ server = client->server;
+ WINPR_ASSERT(server);
+
+ subsystem = server->subsystem;
+ WINPR_ASSERT(subsystem);
+
+ if (freerdp_settings_get_uint32(settings, FreeRDP_ColorDepth) == 24)
+ {
+ if (!freerdp_settings_set_uint32(settings, FreeRDP_ColorDepth, 16)) /* disable 24bpp */
+ return FALSE;
+ }
+
+ const UINT32 MultifragMaxRequestSize =
+ freerdp_settings_get_uint32(settings, FreeRDP_MultifragMaxRequestSize);
+ if (MultifragMaxRequestSize < 0x3F0000)
+ {
+ BOOL rc = freerdp_settings_set_bool(
+ settings, FreeRDP_NSCodec,
+ FALSE); /* NSCodec compressor does not support fragmentation yet */
+ WINPR_ASSERT(rc);
+ }
+
+ WLog_INFO(TAG, "Client from %s is activated (%" PRIu32 "x%" PRIu32 "@%" PRIu32 ")",
+ peer->hostname, freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth),
+ freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight),
+ freerdp_settings_get_uint32(settings, FreeRDP_ColorDepth));
+
+ if (shadow_client_channels_post_connect(client) != CHANNEL_RC_OK)
+ return FALSE;
+
+ shadow_client_mark_invalid(client, 0, NULL);
+ authStatus = -1;
+
+ const char* Username = freerdp_settings_get_string(settings, FreeRDP_Username);
+ const char* Domain = freerdp_settings_get_string(settings, FreeRDP_Domain);
+ const char* Password = freerdp_settings_get_string(settings, FreeRDP_Password);
+
+ if (Username && Password)
+ {
+ if (!freerdp_settings_set_bool(settings, FreeRDP_AutoLogonEnabled, TRUE))
+ return FALSE;
+ }
+
+ if (server->authentication && !freerdp_settings_get_bool(settings, FreeRDP_NlaSecurity))
+ {
+ if (subsystem->Authenticate)
+ {
+ authStatus = subsystem->Authenticate(subsystem, client, Username, Domain, Password);
+ }
+
+ if (authStatus < 0)
+ {
+ WLog_ERR(TAG, "client authentication failure: %d", authStatus);
+ return FALSE;
+ }
+ }
+
+ if (subsystem->ClientConnect)
+ {
+ return subsystem->ClientConnect(subsystem, client);
+ }
+
+ return TRUE;
+}
+
+/* Convert rects in sub rect coordinate to client/surface coordinate */
+static INLINE void shadow_client_convert_rects(rdpShadowClient* client, RECTANGLE_16* dst,
+ const RECTANGLE_16* src, UINT32 numRects)
+{
+ WINPR_ASSERT(client);
+ WINPR_ASSERT(client->server);
+ WINPR_ASSERT(dst);
+ WINPR_ASSERT(src || (numRects == 0));
+
+ if (client->server->shareSubRect)
+ {
+ UINT16 offsetX = client->server->subRect.left;
+ UINT16 offsetY = client->server->subRect.top;
+
+ for (UINT32 i = 0; i < numRects; i++)
+ {
+ const RECTANGLE_16* s = &src[i];
+ RECTANGLE_16* d = &dst[i];
+
+ d->left = s->left + offsetX;
+ d->right = s->right + offsetX;
+ d->top = s->top + offsetY;
+ d->bottom = s->bottom + offsetY;
+ }
+ }
+ else
+ {
+ if (src != dst)
+ {
+ CopyMemory(dst, src, numRects * sizeof(RECTANGLE_16));
+ }
+ }
+}
+
+static BOOL shadow_client_refresh_request(rdpShadowClient* client)
+{
+ wMessage message = { 0 };
+ wMessagePipe* MsgPipe = NULL;
+
+ WINPR_ASSERT(client);
+ WINPR_ASSERT(client->subsystem);
+
+ MsgPipe = client->subsystem->MsgPipe;
+ WINPR_ASSERT(MsgPipe);
+
+ message.id = SHADOW_MSG_IN_REFRESH_REQUEST_ID;
+ message.wParam = NULL;
+ message.lParam = NULL;
+ message.context = (void*)client;
+ message.Free = NULL;
+ return MessageQueue_Dispatch(MsgPipe->In, &message);
+}
+
+static BOOL shadow_client_refresh_rect(rdpContext* context, BYTE count, const RECTANGLE_16* areas)
+{
+ rdpShadowClient* client = (rdpShadowClient*)context;
+ RECTANGLE_16* rects = NULL;
+
+ /* It is invalid if we have area count but no actual area */
+ if (count && !areas)
+ return FALSE;
+
+ if (count)
+ {
+ rects = (RECTANGLE_16*)calloc(count, sizeof(RECTANGLE_16));
+
+ if (!rects)
+ {
+ return FALSE;
+ }
+
+ shadow_client_convert_rects(client, rects, areas, count);
+ shadow_client_mark_invalid(client, count, rects);
+ free(rects);
+ }
+ else
+ {
+ shadow_client_mark_invalid(client, 0, NULL);
+ }
+
+ return shadow_client_refresh_request(client);
+}
+
+static BOOL shadow_client_suppress_output(rdpContext* context, BYTE allow, const RECTANGLE_16* area)
+{
+ rdpShadowClient* client = (rdpShadowClient*)context;
+ RECTANGLE_16 region;
+
+ WINPR_ASSERT(client);
+
+ 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);
+}
+
+static BOOL shadow_client_activate(freerdp_peer* peer)
+{
+ rdpSettings* settings = NULL;
+ rdpShadowClient* client = NULL;
+
+ WINPR_ASSERT(peer);
+
+ client = (rdpShadowClient*)peer->context;
+ WINPR_ASSERT(client);
+
+ settings = peer->context->settings;
+ WINPR_ASSERT(settings);
+
+ /* Resize client if necessary */
+ if (shadow_client_recalc_desktop_size(client))
+ return shadow_send_desktop_resize(client);
+
+ shadow_reset_desktop_resize(client);
+ client->activated = TRUE;
+ client->inLobby = client->mayView ? FALSE : TRUE;
+
+ if (shadow_encoder_reset(client->encoder) < 0)
+ {
+ WLog_ERR(TAG, "Failed to reset encoder");
+ return FALSE;
+ }
+
+ /* Update full screen in next update */
+ return shadow_client_refresh_rect(&client->context, 0, NULL);
+}
+
+static BOOL shadow_client_logon(freerdp_peer* peer, const SEC_WINNT_AUTH_IDENTITY* identity,
+ BOOL automatic)
+{
+ BOOL rc = FALSE;
+ char* user = NULL;
+ char* domain = NULL;
+ char* password = NULL;
+ rdpSettings* settings = NULL;
+
+ WINPR_UNUSED(automatic);
+
+ WINPR_ASSERT(peer);
+ WINPR_ASSERT(identity);
+
+ WINPR_ASSERT(peer->context);
+
+ settings = peer->context->settings;
+ WINPR_ASSERT(settings);
+
+ if (identity->Flags & SEC_WINNT_AUTH_IDENTITY_UNICODE)
+ {
+ if (identity->User)
+ user = ConvertWCharNToUtf8Alloc(identity->User, identity->UserLength, NULL);
+
+ if (identity->Domain)
+ domain = ConvertWCharNToUtf8Alloc(identity->Domain, identity->DomainLength, NULL);
+
+ if (identity->Password)
+ password = ConvertWCharNToUtf8Alloc(identity->Password, identity->PasswordLength, NULL);
+ }
+ else
+ {
+ if (identity->User)
+ user = _strdup((char*)identity->User);
+
+ if (identity->Domain)
+ domain = _strdup((char*)identity->Domain);
+
+ if (identity->Password)
+ password = _strdup((char*)identity->Password);
+ }
+
+ if ((identity->User && !user) || (identity->Domain && !domain) ||
+ (identity->Password && !password))
+ goto fail;
+
+ if (user)
+ freerdp_settings_set_string(settings, FreeRDP_Username, user);
+
+ if (domain)
+ freerdp_settings_set_string(settings, FreeRDP_Domain, domain);
+
+ if (password)
+ freerdp_settings_set_string(settings, FreeRDP_Password, password);
+
+ rc = TRUE;
+fail:
+ free(user);
+ free(domain);
+ free(password);
+ return rc;
+}
+
+static INLINE void shadow_client_common_frame_acknowledge(rdpShadowClient* client, UINT32 frameId)
+{
+ /*
+ * Record the last client acknowledged frame id to
+ * calculate how much frames are in progress.
+ * Some rdp clients (win7 mstsc) skips frame ACK if it is
+ * inactive, we should not expect ACK for each frame.
+ * So it is OK to calculate inflight frame count according to
+ * a latest acknowledged frame id.
+ */
+ WINPR_ASSERT(client);
+ WINPR_ASSERT(client->encoder);
+ client->encoder->lastAckframeId = frameId;
+}
+
+static BOOL shadow_client_surface_frame_acknowledge(rdpContext* context, UINT32 frameId)
+{
+ rdpShadowClient* client = (rdpShadowClient*)context;
+ shadow_client_common_frame_acknowledge(client, frameId);
+ /*
+ * Reset queueDepth for legacy none RDPGFX acknowledge
+ */
+ WINPR_ASSERT(client);
+ WINPR_ASSERT(client->encoder);
+ client->encoder->queueDepth = QUEUE_DEPTH_UNAVAILABLE;
+ return TRUE;
+}
+
+static UINT
+shadow_client_rdpgfx_frame_acknowledge(RdpgfxServerContext* context,
+ const RDPGFX_FRAME_ACKNOWLEDGE_PDU* frameAcknowledge)
+{
+ rdpShadowClient* client = NULL;
+
+ WINPR_ASSERT(context);
+ WINPR_ASSERT(frameAcknowledge);
+
+ client = (rdpShadowClient*)context->custom;
+ shadow_client_common_frame_acknowledge(client, frameAcknowledge->frameId);
+
+ WINPR_ASSERT(client);
+ WINPR_ASSERT(client->encoder);
+ client->encoder->queueDepth = frameAcknowledge->queueDepth;
+ return CHANNEL_RC_OK;
+}
+
+static BOOL shadow_are_caps_filtered(const rdpSettings* settings, UINT32 caps)
+{
+ const UINT32 capList[] = { RDPGFX_CAPVERSION_8, RDPGFX_CAPVERSION_81,
+ RDPGFX_CAPVERSION_10, RDPGFX_CAPVERSION_101,
+ RDPGFX_CAPVERSION_102, RDPGFX_CAPVERSION_103,
+ RDPGFX_CAPVERSION_104, RDPGFX_CAPVERSION_105,
+ RDPGFX_CAPVERSION_106, RDPGFX_CAPVERSION_106_ERR,
+ RDPGFX_CAPVERSION_107 };
+
+ WINPR_ASSERT(settings);
+ const UINT32 filter = freerdp_settings_get_uint32(settings, FreeRDP_GfxCapsFilter);
+
+ for (UINT32 x = 0; x < ARRAYSIZE(capList); x++)
+ {
+ if (caps == capList[x])
+ return (filter & (1 << x)) != 0;
+ }
+
+ return TRUE;
+}
+
+static UINT shadow_client_send_caps_confirm(RdpgfxServerContext* context, rdpShadowClient* client,
+ const RDPGFX_CAPS_CONFIRM_PDU* pdu)
+{
+ WINPR_ASSERT(context);
+ WINPR_ASSERT(client);
+ WINPR_ASSERT(pdu);
+
+ WINPR_ASSERT(context->CapsConfirm);
+ UINT rc = context->CapsConfirm(context, pdu);
+ client->areGfxCapsReady = (rc == CHANNEL_RC_OK);
+ return rc;
+}
+
+static BOOL shadow_client_caps_test_version(RdpgfxServerContext* context, rdpShadowClient* client,
+ BOOL h264, const RDPGFX_CAPSET* capsSets,
+ UINT32 capsSetCount, UINT32 capsVersion, UINT* rc)
+{
+ const rdpSettings* srvSettings = NULL;
+ rdpSettings* clientSettings = NULL;
+
+ WINPR_ASSERT(context);
+ WINPR_ASSERT(client);
+ WINPR_ASSERT(capsSets || (capsSetCount == 0));
+ WINPR_ASSERT(rc);
+
+ WINPR_ASSERT(context->rdpcontext);
+ srvSettings = context->rdpcontext->settings;
+ WINPR_ASSERT(srvSettings);
+
+ clientSettings = client->context.settings;
+ WINPR_ASSERT(clientSettings);
+
+ if (shadow_are_caps_filtered(srvSettings, capsVersion))
+ return FALSE;
+
+ for (UINT32 index = 0; index < capsSetCount; index++)
+ {
+ const RDPGFX_CAPSET* currentCaps = &capsSets[index];
+
+ if (currentCaps->version == capsVersion)
+ {
+ UINT32 flags = 0;
+ BOOL planar = FALSE;
+ BOOL rfx = FALSE;
+ BOOL avc444v2 = FALSE;
+ BOOL avc444 = FALSE;
+ BOOL avc420 = FALSE;
+ BOOL progressive = FALSE;
+ RDPGFX_CAPSET caps = *currentCaps;
+ RDPGFX_CAPS_CONFIRM_PDU pdu = { 0 };
+ pdu.capsSet = &caps;
+
+ flags = pdu.capsSet->flags;
+
+ if (!freerdp_settings_set_bool(clientSettings, FreeRDP_GfxSmallCache,
+ (flags & RDPGFX_CAPS_FLAG_SMALL_CACHE) ? TRUE : FALSE))
+ return FALSE;
+
+ avc444v2 = avc444 = !(flags & RDPGFX_CAPS_FLAG_AVC_DISABLED);
+ if (!freerdp_settings_get_bool(srvSettings, FreeRDP_GfxAVC444v2) || !h264)
+ avc444v2 = FALSE;
+ if (!freerdp_settings_set_bool(clientSettings, FreeRDP_GfxAVC444v2, avc444v2))
+ return FALSE;
+ if (!freerdp_settings_get_bool(srvSettings, FreeRDP_GfxAVC444) || !h264)
+ avc444 = FALSE;
+ if (!freerdp_settings_set_bool(clientSettings, FreeRDP_GfxAVC444, avc444))
+ return FALSE;
+ if (!freerdp_settings_get_bool(srvSettings, FreeRDP_GfxH264) || !h264)
+ avc420 = FALSE;
+ if (!freerdp_settings_set_bool(clientSettings, FreeRDP_GfxH264, avc420))
+ return FALSE;
+
+ progressive = freerdp_settings_get_bool(srvSettings, FreeRDP_GfxProgressive);
+ if (!freerdp_settings_set_bool(clientSettings, FreeRDP_GfxProgressive, progressive))
+ return FALSE;
+ progressive = freerdp_settings_get_bool(srvSettings, FreeRDP_GfxProgressiveV2);
+ if (!freerdp_settings_set_bool(clientSettings, FreeRDP_GfxProgressiveV2, progressive))
+ return FALSE;
+
+ rfx = freerdp_settings_get_bool(srvSettings, FreeRDP_RemoteFxCodec);
+ if (!freerdp_settings_set_bool(clientSettings, FreeRDP_RemoteFxCodec, rfx))
+ return FALSE;
+
+ planar = freerdp_settings_get_bool(srvSettings, FreeRDP_GfxPlanar);
+ if (!freerdp_settings_set_bool(clientSettings, FreeRDP_GfxPlanar, planar))
+ return FALSE;
+
+ if (!avc444v2 && !avc444 && !avc420)
+ pdu.capsSet->flags |= RDPGFX_CAPS_FLAG_AVC_DISABLED;
+
+ *rc = shadow_client_send_caps_confirm(context, client, &pdu);
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+static UINT shadow_client_rdpgfx_caps_advertise(RdpgfxServerContext* context,
+ const RDPGFX_CAPS_ADVERTISE_PDU* capsAdvertise)
+{
+ UINT rc = ERROR_INTERNAL_ERROR;
+ const rdpSettings* srvSettings = NULL;
+ rdpSettings* clientSettings = NULL;
+ BOOL h264 = FALSE;
+
+ UINT32 flags = 0;
+
+ WINPR_ASSERT(context);
+ WINPR_ASSERT(capsAdvertise);
+
+ rdpShadowClient* client = (rdpShadowClient*)context->custom;
+ WINPR_ASSERT(client);
+ WINPR_ASSERT(context->rdpcontext);
+
+ srvSettings = context->rdpcontext->settings;
+ WINPR_ASSERT(srvSettings);
+
+ clientSettings = client->context.settings;
+ WINPR_ASSERT(clientSettings);
+
+#ifdef WITH_GFX_H264
+ h264 =
+ (shadow_encoder_prepare(client->encoder, FREERDP_CODEC_AVC420 | FREERDP_CODEC_AVC444) >= 0);
+#else
+ freerdp_settings_set_bool(clientSettings, FreeRDP_GfxAVC444v2, FALSE);
+ freerdp_settings_set_bool(clientSettings, FreeRDP_GfxAVC444, FALSE);
+ freerdp_settings_set_bool(clientSettings, FreeRDP_GfxH264, FALSE);
+#endif
+
+ /* Request full screen update for new gfx channel */
+ if (!shadow_client_refresh_rect(&client->context, 0, NULL))
+ return rc;
+
+ if (shadow_client_caps_test_version(context, client, h264, capsAdvertise->capsSets,
+ capsAdvertise->capsSetCount, RDPGFX_CAPVERSION_107, &rc))
+ return rc;
+
+ if (shadow_client_caps_test_version(context, client, h264, capsAdvertise->capsSets,
+ capsAdvertise->capsSetCount, RDPGFX_CAPVERSION_106, &rc))
+ return rc;
+
+ if (shadow_client_caps_test_version(context, client, h264, capsAdvertise->capsSets,
+ capsAdvertise->capsSetCount, RDPGFX_CAPVERSION_106_ERR,
+ &rc))
+ return rc;
+
+ if (shadow_client_caps_test_version(context, client, h264, capsAdvertise->capsSets,
+ capsAdvertise->capsSetCount, RDPGFX_CAPVERSION_105, &rc))
+ return rc;
+
+ if (shadow_client_caps_test_version(context, client, h264, capsAdvertise->capsSets,
+ capsAdvertise->capsSetCount, RDPGFX_CAPVERSION_104, &rc))
+ return rc;
+
+ if (shadow_client_caps_test_version(context, client, h264, capsAdvertise->capsSets,
+ capsAdvertise->capsSetCount, RDPGFX_CAPVERSION_103, &rc))
+ return rc;
+
+ if (shadow_client_caps_test_version(context, client, h264, capsAdvertise->capsSets,
+ capsAdvertise->capsSetCount, RDPGFX_CAPVERSION_102, &rc))
+ return rc;
+
+ if (shadow_client_caps_test_version(context, client, h264, capsAdvertise->capsSets,
+ capsAdvertise->capsSetCount, RDPGFX_CAPVERSION_101, &rc))
+ return rc;
+
+ if (shadow_client_caps_test_version(context, client, h264, capsAdvertise->capsSets,
+ capsAdvertise->capsSetCount, RDPGFX_CAPVERSION_10, &rc))
+ return rc;
+
+ if (!shadow_are_caps_filtered(srvSettings, RDPGFX_CAPVERSION_81))
+ {
+ for (UINT32 index = 0; index < capsAdvertise->capsSetCount; index++)
+ {
+ const RDPGFX_CAPSET* currentCaps = &capsAdvertise->capsSets[index];
+
+ if (currentCaps->version == RDPGFX_CAPVERSION_81)
+ {
+ RDPGFX_CAPSET caps = *currentCaps;
+ RDPGFX_CAPS_CONFIRM_PDU pdu;
+ pdu.capsSet = &caps;
+
+ flags = pdu.capsSet->flags;
+
+ freerdp_settings_set_bool(clientSettings, FreeRDP_GfxAVC444v2, FALSE);
+ freerdp_settings_set_bool(clientSettings, FreeRDP_GfxAVC444, FALSE);
+
+ freerdp_settings_set_bool(clientSettings, FreeRDP_GfxThinClient,
+ (flags & RDPGFX_CAPS_FLAG_THINCLIENT) ? TRUE : FALSE);
+ freerdp_settings_set_bool(clientSettings, FreeRDP_GfxSmallCache,
+ (flags & RDPGFX_CAPS_FLAG_SMALL_CACHE) ? TRUE : FALSE);
+
+#ifndef WITH_GFX_H264
+ freerdp_settings_set_bool(clientSettings, FreeRDP_GfxH264, FALSE);
+ pdu.capsSet->flags &= ~RDPGFX_CAPS_FLAG_AVC420_ENABLED;
+#else
+
+ if (h264)
+ freerdp_settings_set_bool(clientSettings, FreeRDP_GfxH264,
+ (flags & RDPGFX_CAPS_FLAG_AVC420_ENABLED) ? TRUE
+ : FALSE);
+ else
+ freerdp_settings_set_bool(clientSettings, FreeRDP_GfxH264, FALSE);
+#endif
+
+ return shadow_client_send_caps_confirm(context, client, &pdu);
+ }
+ }
+ }
+
+ if (!shadow_are_caps_filtered(srvSettings, RDPGFX_CAPVERSION_8))
+ {
+ for (UINT32 index = 0; index < capsAdvertise->capsSetCount; index++)
+ {
+ const RDPGFX_CAPSET* currentCaps = &capsAdvertise->capsSets[index];
+
+ if (currentCaps->version == RDPGFX_CAPVERSION_8)
+ {
+ RDPGFX_CAPSET caps = *currentCaps;
+ RDPGFX_CAPS_CONFIRM_PDU pdu;
+ pdu.capsSet = &caps;
+ flags = pdu.capsSet->flags;
+
+ freerdp_settings_set_bool(clientSettings, FreeRDP_GfxAVC444v2, FALSE);
+ freerdp_settings_set_bool(clientSettings, FreeRDP_GfxAVC444, FALSE);
+ freerdp_settings_set_bool(clientSettings, FreeRDP_GfxH264, FALSE);
+
+ freerdp_settings_set_bool(clientSettings, FreeRDP_GfxThinClient,
+ (flags & RDPGFX_CAPS_FLAG_THINCLIENT) ? TRUE : FALSE);
+ freerdp_settings_set_bool(clientSettings, FreeRDP_GfxSmallCache,
+ (flags & RDPGFX_CAPS_FLAG_SMALL_CACHE) ? TRUE : FALSE);
+
+ return shadow_client_send_caps_confirm(context, client, &pdu);
+ }
+ }
+ }
+
+ return CHANNEL_RC_UNSUPPORTED_VERSION;
+}
+
+static INLINE UINT32 rdpgfx_estimate_h264_avc420(RDPGFX_AVC420_BITMAP_STREAM* havc420)
+{
+ /* H264 metadata + H264 stream. See rdpgfx_write_h264_avc420 */
+ WINPR_ASSERT(havc420);
+ return sizeof(UINT32) /* numRegionRects */
+ + 10 /* regionRects + quantQualityVals */
+ * havc420->meta.numRegionRects +
+ havc420->length;
+}
+
+/**
+ * Function description
+ *
+ * @return TRUE on success
+ */
+static BOOL shadow_client_send_surface_gfx(rdpShadowClient* client, const BYTE* pSrcData,
+ UINT32 nSrcStep, UINT32 SrcFormat, UINT16 nXSrc,
+ UINT16 nYSrc, UINT16 nWidth, UINT16 nHeight)
+{
+ UINT32 id = 0;
+ UINT error = CHANNEL_RC_OK;
+ const rdpContext* context = (const rdpContext*)client;
+ const rdpSettings* settings = NULL;
+ rdpShadowEncoder* encoder = NULL;
+ RDPGFX_SURFACE_COMMAND cmd = { 0 };
+ RDPGFX_START_FRAME_PDU cmdstart = { 0 };
+ RDPGFX_END_FRAME_PDU cmdend = { 0 };
+ SYSTEMTIME sTime = { 0 };
+
+ if (!context || !pSrcData)
+ return FALSE;
+
+ settings = context->settings;
+ encoder = client->encoder;
+
+ if (!settings || !encoder)
+ return FALSE;
+
+ if (client->first_frame)
+ {
+ rfx_context_reset(encoder->rfx, nWidth, nHeight);
+ client->first_frame = FALSE;
+ }
+
+ cmdstart.frameId = shadow_encoder_create_frame_id(encoder);
+ GetSystemTime(&sTime);
+ cmdstart.timestamp = (UINT32)(sTime.wHour << 22U | sTime.wMinute << 16U | sTime.wSecond << 10U |
+ sTime.wMilliseconds);
+ cmdend.frameId = cmdstart.frameId;
+ cmd.surfaceId = client->surfaceId;
+ cmd.format = PIXEL_FORMAT_BGRX32;
+ cmd.left = nXSrc;
+ cmd.top = nYSrc;
+ cmd.right = cmd.left + nWidth;
+ cmd.bottom = cmd.top + nHeight;
+ cmd.width = nWidth;
+ cmd.height = nHeight;
+
+ id = freerdp_settings_get_uint32(settings, FreeRDP_RemoteFxCodecId);
+#ifdef WITH_GFX_H264
+ const BOOL GfxH264 = freerdp_settings_get_bool(settings, FreeRDP_GfxH264);
+ const BOOL GfxAVC444 = freerdp_settings_get_bool(settings, FreeRDP_GfxAVC444);
+ const BOOL GfxAVC444v2 = freerdp_settings_get_bool(settings, FreeRDP_GfxAVC444v2);
+ if (GfxAVC444 || GfxAVC444v2)
+ {
+ INT32 rc = 0;
+ RDPGFX_AVC444_BITMAP_STREAM avc444 = { 0 };
+ RECTANGLE_16 regionRect = { 0 };
+ BYTE version = GfxAVC444v2 ? 2 : 1;
+
+ if (shadow_encoder_prepare(encoder, FREERDP_CODEC_AVC444) < 0)
+ {
+ WLog_ERR(TAG, "Failed to prepare encoder FREERDP_CODEC_AVC444");
+ return FALSE;
+ }
+
+ WINPR_ASSERT(cmd.left <= UINT16_MAX);
+ WINPR_ASSERT(cmd.top <= UINT16_MAX);
+ WINPR_ASSERT(cmd.right <= UINT16_MAX);
+ WINPR_ASSERT(cmd.bottom <= UINT16_MAX);
+ regionRect.left = (UINT16)cmd.left;
+ regionRect.top = (UINT16)cmd.top;
+ regionRect.right = (UINT16)cmd.right;
+ regionRect.bottom = (UINT16)cmd.bottom;
+ rc = avc444_compress(encoder->h264, pSrcData, cmd.format, nSrcStep, nWidth, nHeight,
+ version, &regionRect, &avc444.LC, &avc444.bitstream[0].data,
+ &avc444.bitstream[0].length, &avc444.bitstream[1].data,
+ &avc444.bitstream[1].length, &avc444.bitstream[0].meta,
+ &avc444.bitstream[1].meta);
+ if (rc < 0)
+ {
+ WLog_ERR(TAG, "avc420_compress failed for avc444");
+ return FALSE;
+ }
+
+ /* rc > 0 means new data */
+ if (rc > 0)
+ {
+ avc444.cbAvc420EncodedBitstream1 = rdpgfx_estimate_h264_avc420(&avc444.bitstream[0]);
+ cmd.codecId = GfxAVC444v2 ? RDPGFX_CODECID_AVC444v2 : RDPGFX_CODECID_AVC444;
+ cmd.extra = (void*)&avc444;
+ IFCALLRET(client->rdpgfx->SurfaceFrameCommand, error, client->rdpgfx, &cmd, &cmdstart,
+ &cmdend);
+ }
+
+ free_h264_metablock(&avc444.bitstream[0].meta);
+ free_h264_metablock(&avc444.bitstream[1].meta);
+ if (error)
+ {
+ WLog_ERR(TAG, "SurfaceFrameCommand failed with error %" PRIu32 "", error);
+ return FALSE;
+ }
+ }
+ else if (GfxH264)
+ {
+ INT32 rc = 0;
+ RDPGFX_AVC420_BITMAP_STREAM avc420 = { 0 };
+ RECTANGLE_16 regionRect;
+
+ if (shadow_encoder_prepare(encoder, FREERDP_CODEC_AVC420) < 0)
+ {
+ WLog_ERR(TAG, "Failed to prepare encoder FREERDP_CODEC_AVC420");
+ return FALSE;
+ }
+
+ WINPR_ASSERT(cmd.left <= UINT16_MAX);
+ WINPR_ASSERT(cmd.top <= UINT16_MAX);
+ WINPR_ASSERT(cmd.right <= UINT16_MAX);
+ WINPR_ASSERT(cmd.bottom <= UINT16_MAX);
+ regionRect.left = (UINT16)cmd.left;
+ regionRect.top = (UINT16)cmd.top;
+ regionRect.right = (UINT16)cmd.right;
+ regionRect.bottom = (UINT16)cmd.bottom;
+ rc = avc420_compress(encoder->h264, pSrcData, cmd.format, nSrcStep, nWidth, nHeight,
+ &regionRect, &avc420.data, &avc420.length, &avc420.meta);
+ if (rc < 0)
+ {
+ WLog_ERR(TAG, "avc420_compress failed");
+ return FALSE;
+ }
+
+ /* rc > 0 means new data */
+ if (rc > 0)
+ {
+ cmd.codecId = RDPGFX_CODECID_AVC420;
+ cmd.extra = (void*)&avc420;
+
+ IFCALLRET(client->rdpgfx->SurfaceFrameCommand, error, client->rdpgfx, &cmd, &cmdstart,
+ &cmdend);
+ }
+ free_h264_metablock(&avc420.meta);
+
+ if (error)
+ {
+ WLog_ERR(TAG, "SurfaceFrameCommand failed with error %" PRIu32 "", error);
+ return FALSE;
+ }
+ }
+ else
+#endif
+ if (freerdp_settings_get_bool(settings, FreeRDP_RemoteFxCodec) && (id != 0))
+ {
+ BOOL rc = 0;
+ wStream* s = NULL;
+ RFX_RECT rect;
+
+ if (shadow_encoder_prepare(encoder, FREERDP_CODEC_REMOTEFX) < 0)
+ {
+ WLog_ERR(TAG, "Failed to prepare encoder FREERDP_CODEC_REMOTEFX");
+ return FALSE;
+ }
+
+ s = Stream_New(NULL, 1024);
+ WINPR_ASSERT(s);
+
+ WINPR_ASSERT(cmd.left <= UINT16_MAX);
+ WINPR_ASSERT(cmd.top <= UINT16_MAX);
+ WINPR_ASSERT(cmd.right <= UINT16_MAX);
+ WINPR_ASSERT(cmd.bottom <= UINT16_MAX);
+ rect.x = (UINT16)cmd.left;
+ rect.y = (UINT16)cmd.top;
+ rect.width = (UINT16)cmd.right - cmd.left;
+ rect.height = (UINT16)cmd.bottom - cmd.top;
+
+ rc = rfx_compose_message(encoder->rfx, s, &rect, 1, pSrcData, nWidth, nHeight, nSrcStep);
+
+ if (!rc)
+ {
+ WLog_ERR(TAG, "rfx_compose_message failed");
+ Stream_Free(s, TRUE);
+ return FALSE;
+ }
+
+ /* rc > 0 means new data */
+ if (rc > 0)
+ {
+ const size_t pos = Stream_GetPosition(s);
+ WINPR_ASSERT(pos <= UINT32_MAX);
+
+ cmd.codecId = RDPGFX_CODECID_CAVIDEO;
+ cmd.data = Stream_Buffer(s);
+ cmd.length = (UINT32)pos;
+
+ IFCALLRET(client->rdpgfx->SurfaceFrameCommand, error, client->rdpgfx, &cmd, &cmdstart,
+ &cmdend);
+ }
+
+ Stream_Free(s, TRUE);
+ if (error)
+ {
+ WLog_ERR(TAG, "SurfaceFrameCommand failed with error %" PRIu32 "", error);
+ return FALSE;
+ }
+ }
+ else if (freerdp_settings_get_bool(settings, FreeRDP_GfxProgressive))
+ {
+ INT32 rc = 0;
+ REGION16 region;
+ RECTANGLE_16 regionRect;
+
+ if (shadow_encoder_prepare(encoder, FREERDP_CODEC_PROGRESSIVE) < 0)
+ {
+ WLog_ERR(TAG, "Failed to prepare encoder FREERDP_CODEC_PROGRESSIVE");
+ return FALSE;
+ }
+
+ WINPR_ASSERT(cmd.left <= UINT16_MAX);
+ WINPR_ASSERT(cmd.top <= UINT16_MAX);
+ WINPR_ASSERT(cmd.right <= UINT16_MAX);
+ WINPR_ASSERT(cmd.bottom <= UINT16_MAX);
+ regionRect.left = (UINT16)cmd.left;
+ regionRect.top = (UINT16)cmd.top;
+ regionRect.right = (UINT16)cmd.right;
+ regionRect.bottom = (UINT16)cmd.bottom;
+ region16_init(&region);
+ region16_union_rect(&region, &region, &regionRect);
+ rc = progressive_compress(encoder->progressive, pSrcData, nSrcStep * nHeight, cmd.format,
+ nWidth, nHeight, nSrcStep, &region, &cmd.data, &cmd.length);
+ region16_uninit(&region);
+ if (rc < 0)
+ {
+ WLog_ERR(TAG, "progressive_compress failed");
+ return FALSE;
+ }
+
+ /* rc > 0 means new data */
+ if (rc > 0)
+ {
+ cmd.codecId = RDPGFX_CODECID_CAPROGRESSIVE;
+
+ IFCALLRET(client->rdpgfx->SurfaceFrameCommand, error, client->rdpgfx, &cmd, &cmdstart,
+ &cmdend);
+ }
+
+ if (error)
+ {
+ WLog_ERR(TAG, "SurfaceFrameCommand failed with error %" PRIu32 "", error);
+ return FALSE;
+ }
+ }
+ else if (freerdp_settings_get_bool(settings, FreeRDP_GfxPlanar))
+ {
+ BOOL rc = 0;
+ const UINT32 w = cmd.right - cmd.left;
+ const UINT32 h = cmd.bottom - cmd.top;
+ const BYTE* src =
+ &pSrcData[cmd.top * nSrcStep + cmd.left * FreeRDPGetBytesPerPixel(SrcFormat)];
+ if (shadow_encoder_prepare(encoder, FREERDP_CODEC_PLANAR) < 0)
+ {
+ WLog_ERR(TAG, "Failed to prepare encoder FREERDP_CODEC_PLANAR");
+ return FALSE;
+ }
+
+ rc = freerdp_bitmap_planar_context_reset(encoder->planar, w, h);
+ WINPR_ASSERT(rc);
+ freerdp_planar_topdown_image(encoder->planar, TRUE);
+
+ cmd.data = freerdp_bitmap_compress_planar(encoder->planar, src, SrcFormat, w, h, nSrcStep,
+ NULL, &cmd.length);
+ WINPR_ASSERT(cmd.data || (cmd.length == 0));
+
+ cmd.codecId = RDPGFX_CODECID_PLANAR;
+
+ IFCALLRET(client->rdpgfx->SurfaceFrameCommand, error, client->rdpgfx, &cmd, &cmdstart,
+ &cmdend);
+ free(cmd.data);
+ if (error)
+ {
+ WLog_ERR(TAG, "SurfaceFrameCommand failed with error %" PRIu32 "", error);
+ return FALSE;
+ }
+ }
+ else
+ {
+ BOOL rc = 0;
+ const UINT32 w = cmd.right - cmd.left;
+ const UINT32 h = cmd.bottom - cmd.top;
+ const UINT32 length = w * 4 * h;
+ BYTE* data = malloc(length);
+
+ WINPR_ASSERT(data);
+
+ rc = freerdp_image_copy(data, PIXEL_FORMAT_BGRA32, 0, 0, 0, w, h, pSrcData, SrcFormat,
+ nSrcStep, cmd.left, cmd.top, NULL, 0);
+ WINPR_ASSERT(rc);
+
+ cmd.data = data;
+ cmd.length = length;
+ cmd.codecId = RDPGFX_CODECID_UNCOMPRESSED;
+
+ IFCALLRET(client->rdpgfx->SurfaceFrameCommand, error, client->rdpgfx, &cmd, &cmdstart,
+ &cmdend);
+ free(data);
+ if (error)
+ {
+ WLog_ERR(TAG, "SurfaceFrameCommand failed with error %" PRIu32 "", error);
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+/**
+ * Function description
+ *
+ * @return TRUE on success
+ */
+static BOOL shadow_client_send_surface_bits(rdpShadowClient* client, BYTE* pSrcData,
+ UINT32 nSrcStep, UINT16 nXSrc, UINT16 nYSrc,
+ UINT16 nWidth, UINT16 nHeight)
+{
+ BOOL ret = TRUE;
+ BOOL first = 0;
+ BOOL last = 0;
+ wStream* s = NULL;
+ size_t numMessages = 0;
+ UINT32 frameId = 0;
+ rdpUpdate* update = NULL;
+ rdpContext* context = (rdpContext*)client;
+ rdpSettings* settings = NULL;
+ rdpShadowEncoder* encoder = NULL;
+ SURFACE_BITS_COMMAND cmd = { 0 };
+ UINT32 nsID = 0;
+ UINT32 rfxID = 0;
+
+ if (!context || !pSrcData)
+ return FALSE;
+
+ update = context->update;
+ settings = context->settings;
+ encoder = client->encoder;
+
+ if (!update || !settings || !encoder)
+ return FALSE;
+
+ if (encoder->frameAck)
+ frameId = shadow_encoder_create_frame_id(encoder);
+
+ nsID = freerdp_settings_get_uint32(settings, FreeRDP_NSCodecId);
+ rfxID = freerdp_settings_get_uint32(settings, FreeRDP_RemoteFxCodecId);
+ if (freerdp_settings_get_bool(settings, FreeRDP_RemoteFxCodec) && (rfxID != 0))
+ {
+ RFX_RECT rect = { 0 };
+
+ if (shadow_encoder_prepare(encoder, FREERDP_CODEC_REMOTEFX) < 0)
+ {
+ WLog_ERR(TAG, "Failed to prepare encoder FREERDP_CODEC_REMOTEFX");
+ return FALSE;
+ }
+
+ s = encoder->bs;
+ rect.x = nXSrc;
+ rect.y = nYSrc;
+ rect.width = nWidth;
+ rect.height = nHeight;
+
+ const UINT32 MultifragMaxRequestSize =
+ freerdp_settings_get_uint32(settings, FreeRDP_MultifragMaxRequestSize);
+ RFX_MESSAGE_LIST* messages =
+ rfx_encode_messages(encoder->rfx, &rect, 1, pSrcData,
+ freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth),
+ freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight),
+ nSrcStep, &numMessages, MultifragMaxRequestSize);
+ if (!messages)
+ {
+ WLog_ERR(TAG, "rfx_encode_messages failed");
+ return FALSE;
+ }
+
+ cmd.cmdType = CMDTYPE_STREAM_SURFACE_BITS;
+ WINPR_ASSERT(rfxID <= UINT16_MAX);
+ cmd.bmp.codecID = (UINT16)rfxID;
+ cmd.destLeft = 0;
+ cmd.destTop = 0;
+ cmd.destRight = freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth);
+ cmd.destBottom = freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight);
+ cmd.bmp.bpp = 32;
+ cmd.bmp.flags = 0;
+ WINPR_ASSERT(freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth) <= UINT16_MAX);
+ WINPR_ASSERT(freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight) <= UINT16_MAX);
+ cmd.bmp.width = (UINT16)freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth);
+ cmd.bmp.height = (UINT16)freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight);
+ cmd.skipCompression = TRUE;
+
+ for (size_t i = 0; i < numMessages; i++)
+ {
+ Stream_SetPosition(s, 0);
+
+ const RFX_MESSAGE* msg = rfx_message_list_get(messages, i);
+ if (!rfx_write_message(encoder->rfx, s, msg))
+ {
+ rfx_message_list_free(messages);
+ WLog_ERR(TAG, "rfx_write_message failed");
+ ret = FALSE;
+ break;
+ }
+
+ WINPR_ASSERT(Stream_GetPosition(s) <= UINT32_MAX);
+ cmd.bmp.bitmapDataLength = (UINT32)Stream_GetPosition(s);
+ cmd.bmp.bitmapData = Stream_Buffer(s);
+ first = (i == 0) ? TRUE : FALSE;
+ last = ((i + 1) == numMessages) ? TRUE : FALSE;
+
+ if (!encoder->frameAck)
+ IFCALLRET(update->SurfaceBits, ret, update->context, &cmd);
+ else
+ IFCALLRET(update->SurfaceFrameBits, ret, update->context, &cmd, first, last,
+ frameId);
+
+ if (!ret)
+ {
+ WLog_ERR(TAG, "Send surface bits(RemoteFxCodec) failed");
+ break;
+ }
+ }
+
+ rfx_message_list_free(messages);
+ }
+ if (freerdp_settings_get_bool(settings, FreeRDP_NSCodec) && (nsID != 0))
+ {
+ if (shadow_encoder_prepare(encoder, FREERDP_CODEC_NSCODEC) < 0)
+ {
+ WLog_ERR(TAG, "Failed to prepare encoder FREERDP_CODEC_NSCODEC");
+ return FALSE;
+ }
+
+ s = encoder->bs;
+ Stream_SetPosition(s, 0);
+ pSrcData = &pSrcData[(nYSrc * nSrcStep) + (nXSrc * 4)];
+ nsc_compose_message(encoder->nsc, s, pSrcData, nWidth, nHeight, nSrcStep);
+ cmd.cmdType = CMDTYPE_SET_SURFACE_BITS;
+ cmd.bmp.bpp = 32;
+ WINPR_ASSERT(nsID <= UINT16_MAX);
+ cmd.bmp.codecID = (UINT16)nsID;
+ cmd.destLeft = nXSrc;
+ cmd.destTop = nYSrc;
+ cmd.destRight = cmd.destLeft + nWidth;
+ cmd.destBottom = cmd.destTop + nHeight;
+ cmd.bmp.width = nWidth;
+ cmd.bmp.height = nHeight;
+ WINPR_ASSERT(Stream_GetPosition(s) <= UINT32_MAX);
+ cmd.bmp.bitmapDataLength = (UINT32)Stream_GetPosition(s);
+ cmd.bmp.bitmapData = Stream_Buffer(s);
+ first = TRUE;
+ last = TRUE;
+
+ if (!encoder->frameAck)
+ IFCALLRET(update->SurfaceBits, ret, update->context, &cmd);
+ else
+ IFCALLRET(update->SurfaceFrameBits, ret, update->context, &cmd, first, last, frameId);
+
+ if (!ret)
+ {
+ WLog_ERR(TAG, "Send surface bits(NSCodec) failed");
+ }
+ }
+
+ return ret;
+}
+
+/**
+ * Function description
+ *
+ * @return TRUE on success
+ */
+static BOOL shadow_client_send_bitmap_update(rdpShadowClient* client, BYTE* pSrcData,
+ UINT32 nSrcStep, UINT16 nXSrc, UINT16 nYSrc,
+ UINT16 nWidth, UINT16 nHeight)
+{
+ BOOL ret = TRUE;
+ BYTE* data = NULL;
+ BYTE* buffer = NULL;
+ UINT32 k = 0;
+ UINT32 yIdx = 0;
+ UINT32 xIdx = 0;
+ UINT32 rows = 0;
+ UINT32 cols = 0;
+ UINT32 DstSize = 0;
+ UINT32 SrcFormat = 0;
+ BITMAP_DATA* bitmap = NULL;
+ rdpUpdate* update = NULL;
+ rdpContext* context = (rdpContext*)client;
+ rdpSettings* settings = NULL;
+ UINT32 totalBitmapSize = 0;
+ UINT32 updateSizeEstimate = 0;
+ BITMAP_DATA* bitmapData = NULL;
+ BITMAP_UPDATE bitmapUpdate;
+ rdpShadowEncoder* encoder = NULL;
+
+ if (!context || !pSrcData)
+ return FALSE;
+
+ update = context->update;
+ settings = context->settings;
+ encoder = client->encoder;
+
+ if (!update || !settings || !encoder)
+ return FALSE;
+
+ const UINT32 maxUpdateSize =
+ freerdp_settings_get_uint32(settings, FreeRDP_MultifragMaxRequestSize);
+ if (freerdp_settings_get_uint32(settings, FreeRDP_ColorDepth) < 32)
+ {
+ if (shadow_encoder_prepare(encoder, FREERDP_CODEC_INTERLEAVED) < 0)
+ {
+ WLog_ERR(TAG, "Failed to prepare encoder FREERDP_CODEC_INTERLEAVED");
+ return FALSE;
+ }
+ }
+ else
+ {
+ if (shadow_encoder_prepare(encoder, FREERDP_CODEC_PLANAR) < 0)
+ {
+ WLog_ERR(TAG, "Failed to prepare encoder FREERDP_CODEC_PLANAR");
+ return FALSE;
+ }
+ }
+
+ SrcFormat = PIXEL_FORMAT_BGRX32;
+
+ if ((nXSrc % 4) != 0)
+ {
+ nWidth += (nXSrc % 4);
+ nXSrc -= (nXSrc % 4);
+ }
+
+ if ((nYSrc % 4) != 0)
+ {
+ nHeight += (nYSrc % 4);
+ nYSrc -= (nYSrc % 4);
+ }
+
+ rows = (nHeight / 64) + ((nHeight % 64) ? 1 : 0);
+ cols = (nWidth / 64) + ((nWidth % 64) ? 1 : 0);
+ k = 0;
+ totalBitmapSize = 0;
+ bitmapUpdate.number = rows * cols;
+
+ if (!(bitmapData = (BITMAP_DATA*)calloc(bitmapUpdate.number, sizeof(BITMAP_DATA))))
+ return FALSE;
+
+ bitmapUpdate.rectangles = bitmapData;
+
+ if ((nWidth % 4) != 0)
+ {
+ nWidth += (4 - (nWidth % 4));
+ }
+
+ if ((nHeight % 4) != 0)
+ {
+ nHeight += (4 - (nHeight % 4));
+ }
+
+ for (yIdx = 0; yIdx < rows; yIdx++)
+ {
+ for (xIdx = 0; xIdx < cols; xIdx++)
+ {
+ bitmap = &bitmapData[k];
+ bitmap->width = 64;
+ bitmap->height = 64;
+ bitmap->destLeft = nXSrc + (xIdx * 64);
+ bitmap->destTop = nYSrc + (yIdx * 64);
+
+ if ((INT64)(bitmap->destLeft + bitmap->width) > (nXSrc + nWidth))
+ bitmap->width = (UINT32)(nXSrc + nWidth) - bitmap->destLeft;
+
+ if ((INT64)(bitmap->destTop + bitmap->height) > (nYSrc + nHeight))
+ bitmap->height = (UINT32)(nYSrc + nHeight) - bitmap->destTop;
+
+ bitmap->destRight = bitmap->destLeft + bitmap->width - 1;
+ bitmap->destBottom = bitmap->destTop + bitmap->height - 1;
+ bitmap->compressed = TRUE;
+
+ if ((bitmap->width < 4) || (bitmap->height < 4))
+ continue;
+
+ if (freerdp_settings_get_uint32(settings, FreeRDP_ColorDepth) < 32)
+ {
+ UINT32 bitsPerPixel = freerdp_settings_get_uint32(settings, FreeRDP_ColorDepth);
+ UINT32 bytesPerPixel = (bitsPerPixel + 7) / 8;
+ DstSize = 64 * 64 * 4;
+ buffer = encoder->grid[k];
+ interleaved_compress(encoder->interleaved, buffer, &DstSize, bitmap->width,
+ bitmap->height, pSrcData, SrcFormat, nSrcStep,
+ bitmap->destLeft, bitmap->destTop, NULL, bitsPerPixel);
+ bitmap->bitmapDataStream = buffer;
+ bitmap->bitmapLength = DstSize;
+ bitmap->bitsPerPixel = bitsPerPixel;
+ bitmap->cbScanWidth = bitmap->width * bytesPerPixel;
+ bitmap->cbUncompressedSize = bitmap->width * bitmap->height * bytesPerPixel;
+ }
+ else
+ {
+ UINT32 dstSize = 0;
+ buffer = encoder->grid[k];
+ data = &pSrcData[(bitmap->destTop * nSrcStep) + (bitmap->destLeft * 4)];
+
+ buffer =
+ freerdp_bitmap_compress_planar(encoder->planar, data, SrcFormat, bitmap->width,
+ bitmap->height, nSrcStep, buffer, &dstSize);
+ bitmap->bitmapDataStream = buffer;
+ bitmap->bitmapLength = dstSize;
+ bitmap->bitsPerPixel = 32;
+ bitmap->cbScanWidth = bitmap->width * 4;
+ bitmap->cbUncompressedSize = bitmap->width * bitmap->height * 4;
+ }
+
+ bitmap->cbCompFirstRowSize = 0;
+ bitmap->cbCompMainBodySize = bitmap->bitmapLength;
+ totalBitmapSize += bitmap->bitmapLength;
+ k++;
+ }
+ }
+
+ bitmapUpdate.number = k;
+ updateSizeEstimate = totalBitmapSize + (k * bitmapUpdate.number) + 16;
+
+ if (updateSizeEstimate > maxUpdateSize)
+ {
+ UINT32 i = 0;
+ UINT32 j = 0;
+ UINT32 updateSize = 0;
+ UINT32 newUpdateSize = 0;
+ BITMAP_DATA* fragBitmapData = NULL;
+
+ if (k > 0)
+ fragBitmapData = (BITMAP_DATA*)calloc(k, sizeof(BITMAP_DATA));
+
+ if (!fragBitmapData)
+ {
+ WLog_ERR(TAG, "Failed to allocate memory for fragBitmapData");
+ ret = FALSE;
+ goto out;
+ }
+
+ bitmapUpdate.rectangles = fragBitmapData;
+ i = j = 0;
+ updateSize = 1024;
+
+ while (i < k)
+ {
+ newUpdateSize = updateSize + (bitmapData[i].bitmapLength + 16);
+
+ if (newUpdateSize < maxUpdateSize)
+ {
+ CopyMemory(&fragBitmapData[j++], &bitmapData[i++], sizeof(BITMAP_DATA));
+ updateSize = newUpdateSize;
+ }
+
+ if ((newUpdateSize >= maxUpdateSize) || (i + 1) >= k)
+ {
+ bitmapUpdate.number = j;
+ IFCALLRET(update->BitmapUpdate, ret, context, &bitmapUpdate);
+
+ if (!ret)
+ {
+ WLog_ERR(TAG, "BitmapUpdate failed");
+ break;
+ }
+
+ updateSize = 1024;
+ j = 0;
+ }
+ }
+
+ free(fragBitmapData);
+ }
+ else
+ {
+ IFCALLRET(update->BitmapUpdate, ret, context, &bitmapUpdate);
+
+ if (!ret)
+ {
+ WLog_ERR(TAG, "BitmapUpdate failed");
+ }
+ }
+
+out:
+ free(bitmapData);
+ return ret;
+}
+
+/**
+ * Function description
+ *
+ * @return TRUE on success (or nothing need to be updated)
+ */
+static BOOL shadow_client_send_surface_update(rdpShadowClient* client, SHADOW_GFX_STATUS* pStatus)
+{
+ BOOL ret = TRUE;
+ INT64 nXSrc = 0;
+ INT64 nYSrc = 0;
+ INT64 nWidth = 0;
+ INT64 nHeight = 0;
+ rdpContext* context = (rdpContext*)client;
+ rdpSettings* settings = NULL;
+ rdpShadowServer* server = NULL;
+ rdpShadowSurface* surface = NULL;
+ REGION16 invalidRegion;
+ RECTANGLE_16 surfaceRect;
+ const RECTANGLE_16* extents = NULL;
+ BYTE* pSrcData = NULL;
+ UINT32 nSrcStep = 0;
+ UINT32 SrcFormat = 0;
+ UINT32 numRects = 0;
+ const RECTANGLE_16* rects = NULL;
+
+ if (!context || !pStatus)
+ return FALSE;
+
+ settings = context->settings;
+ server = client->server;
+
+ if (!settings || !server)
+ return FALSE;
+
+ surface = client->inLobby ? server->lobby : server->surface;
+
+ if (!surface)
+ return FALSE;
+
+ EnterCriticalSection(&(client->lock));
+ region16_init(&invalidRegion);
+ region16_copy(&invalidRegion, &(client->invalidRegion));
+ region16_clear(&(client->invalidRegion));
+ LeaveCriticalSection(&(client->lock));
+
+ EnterCriticalSection(&surface->lock);
+ rects = region16_rects(&(surface->invalidRegion), &numRects);
+
+ for (UINT32 index = 0; index < numRects; index++)
+ region16_union_rect(&invalidRegion, &invalidRegion, &rects[index]);
+
+ surfaceRect.left = 0;
+ surfaceRect.top = 0;
+ WINPR_ASSERT(surface->width <= UINT16_MAX);
+ WINPR_ASSERT(surface->height <= UINT16_MAX);
+ surfaceRect.right = (UINT16)surface->width;
+ surfaceRect.bottom = (UINT16)surface->height;
+ region16_intersect_rect(&invalidRegion, &invalidRegion, &surfaceRect);
+
+ if (server->shareSubRect)
+ {
+ region16_intersect_rect(&invalidRegion, &invalidRegion, &(server->subRect));
+ }
+
+ if (region16_is_empty(&invalidRegion))
+ {
+ /* No image region need to be updated. Success */
+ goto out;
+ }
+
+ extents = region16_extents(&invalidRegion);
+ nXSrc = extents->left;
+ nYSrc = extents->top;
+ nWidth = extents->right - extents->left;
+ nHeight = extents->bottom - extents->top;
+ pSrcData = surface->data;
+ nSrcStep = surface->scanline;
+ SrcFormat = surface->format;
+
+ /* Move to new pSrcData / nXSrc / nYSrc according to sub rect */
+ if (server->shareSubRect)
+ {
+ INT32 subX = 0;
+ INT32 subY = 0;
+ subX = server->subRect.left;
+ subY = server->subRect.top;
+ nXSrc -= subX;
+ nYSrc -= subY;
+ WINPR_ASSERT(nXSrc >= 0);
+ WINPR_ASSERT(nXSrc <= UINT16_MAX);
+ WINPR_ASSERT(nYSrc >= 0);
+ WINPR_ASSERT(nYSrc <= UINT16_MAX);
+ pSrcData = &pSrcData[((UINT16)subY * nSrcStep) + ((UINT16)subX * 4U)];
+ }
+
+ // WLog_INFO(TAG, "shadow_client_send_surface_update: x: %" PRId64 " y: %" PRId64 " width: %"
+ // PRId64 " height: %" PRId64 " right: %" PRId64 " bottom: %" PRId64, nXSrc, nYSrc, nWidth,
+ // nHeight, nXSrc + nWidth, nYSrc + nHeight);
+
+ if (freerdp_settings_get_bool(settings, FreeRDP_SupportGraphicsPipeline))
+ {
+ if (pStatus->gfxOpened && client->areGfxCapsReady)
+ {
+ /* GFX/h264 always full screen encoded */
+ nWidth = freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth);
+ nHeight = freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight);
+
+ /* Create primary surface if have not */
+ if (!pStatus->gfxSurfaceCreated)
+ {
+ /* Only init surface when we have h264 supported */
+ if (!(ret = shadow_client_rdpgfx_reset_graphic(client)))
+ goto out;
+
+ if (!(ret = shadow_client_rdpgfx_new_surface(client)))
+ goto out;
+
+ pStatus->gfxSurfaceCreated = TRUE;
+ }
+
+ WINPR_ASSERT(nWidth >= 0);
+ WINPR_ASSERT(nWidth <= UINT16_MAX);
+ WINPR_ASSERT(nHeight >= 0);
+ WINPR_ASSERT(nHeight <= UINT16_MAX);
+ ret = shadow_client_send_surface_gfx(client, pSrcData, nSrcStep, SrcFormat, 0, 0,
+ (UINT16)nWidth, (UINT16)nHeight);
+ }
+ else
+ {
+ ret = TRUE;
+ }
+ }
+ else if (freerdp_settings_get_bool(settings, FreeRDP_RemoteFxCodec) ||
+ freerdp_settings_get_bool(settings, FreeRDP_NSCodec))
+ {
+ WINPR_ASSERT(nXSrc >= 0);
+ WINPR_ASSERT(nXSrc <= UINT16_MAX);
+ WINPR_ASSERT(nYSrc >= 0);
+ WINPR_ASSERT(nYSrc <= UINT16_MAX);
+ WINPR_ASSERT(nWidth >= 0);
+ WINPR_ASSERT(nWidth <= UINT16_MAX);
+ WINPR_ASSERT(nHeight >= 0);
+ WINPR_ASSERT(nHeight <= UINT16_MAX);
+ ret = shadow_client_send_surface_bits(client, pSrcData, nSrcStep, (UINT16)nXSrc,
+ (UINT16)nYSrc, (UINT16)nWidth, (UINT16)nHeight);
+ }
+ else
+ {
+ WINPR_ASSERT(nXSrc >= 0);
+ WINPR_ASSERT(nXSrc <= UINT16_MAX);
+ WINPR_ASSERT(nYSrc >= 0);
+ WINPR_ASSERT(nYSrc <= UINT16_MAX);
+ WINPR_ASSERT(nWidth >= 0);
+ WINPR_ASSERT(nWidth <= UINT16_MAX);
+ WINPR_ASSERT(nHeight >= 0);
+ WINPR_ASSERT(nHeight <= UINT16_MAX);
+ ret = shadow_client_send_bitmap_update(client, pSrcData, nSrcStep, (UINT16)nXSrc,
+ (UINT16)nYSrc, (UINT16)nWidth, (UINT16)nHeight);
+ }
+
+out:
+ LeaveCriticalSection(&surface->lock);
+ region16_uninit(&invalidRegion);
+ return ret;
+}
+
+/**
+ * Function description
+ * Notify client for resize. The new desktop width/height
+ * should have already been updated in rdpSettings.
+ *
+ * @return TRUE on success
+ */
+static BOOL shadow_client_send_resize(rdpShadowClient* client, SHADOW_GFX_STATUS* pStatus)
+{
+ rdpContext* context = (rdpContext*)client;
+ rdpSettings* settings = NULL;
+ freerdp_peer* peer = NULL;
+
+ if (!context || !pStatus)
+ return FALSE;
+
+ peer = context->peer;
+ settings = context->settings;
+
+ if (!peer || !settings)
+ return FALSE;
+
+ /**
+ * 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;
+
+ /* Close Gfx surfaces */
+ if (pStatus->gfxSurfaceCreated)
+ {
+ if (!shadow_client_rdpgfx_release_surface(client))
+ return FALSE;
+
+ pStatus->gfxSurfaceCreated = FALSE;
+ }
+
+ /* Send Resize */
+ if (!shadow_send_desktop_resize(client))
+ return FALSE;
+ shadow_reset_desktop_resize(client);
+
+ /* Clear my invalidRegion. shadow_client_activate refreshes fullscreen */
+ EnterCriticalSection(&(client->lock));
+ region16_clear(&(client->invalidRegion));
+ LeaveCriticalSection(&(client->lock));
+ return TRUE;
+}
+
+/**
+ * Function description
+ * Mark invalid region for client
+ *
+ * @return TRUE on success
+ */
+static BOOL shadow_client_surface_update(rdpShadowClient* client, REGION16* region)
+{
+ UINT32 numRects = 0;
+ const RECTANGLE_16* rects = NULL;
+ rects = region16_rects(region, &numRects);
+ shadow_client_mark_invalid(client, numRects, rects);
+ return TRUE;
+}
+
+/**
+ * Function description
+ * Only union invalid region from server surface
+ *
+ * @return TRUE on success
+ */
+static INLINE BOOL shadow_client_no_surface_update(rdpShadowClient* client,
+ SHADOW_GFX_STATUS* pStatus)
+{
+ rdpShadowServer* server = NULL;
+ rdpShadowSurface* surface = NULL;
+ WINPR_UNUSED(pStatus);
+ WINPR_ASSERT(client);
+ server = client->server;
+ WINPR_ASSERT(server);
+ surface = client->inLobby ? server->lobby : server->surface;
+ return shadow_client_surface_update(client, &(surface->invalidRegion));
+}
+
+static int shadow_client_subsystem_process_message(rdpShadowClient* client, wMessage* message)
+{
+ rdpContext* context = (rdpContext*)client;
+ rdpUpdate* update = NULL;
+
+ WINPR_ASSERT(message);
+ WINPR_ASSERT(context);
+ update = context->update;
+ WINPR_ASSERT(update);
+
+ /* FIXME: the pointer updates appear to be broken when used with bulk compression and mstsc */
+
+ switch (message->id)
+ {
+ case SHADOW_MSG_OUT_POINTER_POSITION_UPDATE_ID:
+ {
+ POINTER_POSITION_UPDATE pointerPosition;
+ const SHADOW_MSG_OUT_POINTER_POSITION_UPDATE* msg =
+ (const SHADOW_MSG_OUT_POINTER_POSITION_UPDATE*)message->wParam;
+ pointerPosition.xPos = msg->xPos;
+ pointerPosition.yPos = msg->yPos;
+
+ WINPR_ASSERT(client->server);
+ if (client->server->shareSubRect)
+ {
+ pointerPosition.xPos -= client->server->subRect.left;
+ pointerPosition.yPos -= client->server->subRect.top;
+ }
+
+ if (client->activated)
+ {
+ if ((msg->xPos != client->pointerX) || (msg->yPos != client->pointerY))
+ {
+ WINPR_ASSERT(update->pointer);
+ IFCALL(update->pointer->PointerPosition, context, &pointerPosition);
+ client->pointerX = msg->xPos;
+ client->pointerY = msg->yPos;
+ }
+ }
+
+ break;
+ }
+
+ case SHADOW_MSG_OUT_POINTER_ALPHA_UPDATE_ID:
+ {
+ POINTER_NEW_UPDATE pointerNew = { 0 };
+ POINTER_COLOR_UPDATE* pointerColor = { 0 };
+ POINTER_CACHED_UPDATE pointerCached = { 0 };
+ const SHADOW_MSG_OUT_POINTER_ALPHA_UPDATE* msg =
+ (const SHADOW_MSG_OUT_POINTER_ALPHA_UPDATE*)message->wParam;
+
+ WINPR_ASSERT(msg);
+ pointerNew.xorBpp = 24;
+ pointerColor = &(pointerNew.colorPtrAttr);
+ pointerColor->cacheIndex = 0;
+ pointerColor->hotSpotX = msg->xHot;
+ pointerColor->hotSpotY = msg->yHot;
+ pointerColor->width = msg->width;
+ pointerColor->height = msg->height;
+ pointerColor->lengthAndMask = msg->lengthAndMask;
+ pointerColor->lengthXorMask = msg->lengthXorMask;
+ pointerColor->xorMaskData = msg->xorMaskData;
+ pointerColor->andMaskData = msg->andMaskData;
+ pointerCached.cacheIndex = pointerColor->cacheIndex;
+
+ if (client->activated)
+ {
+ IFCALL(update->pointer->PointerNew, context, &pointerNew);
+ IFCALL(update->pointer->PointerCached, context, &pointerCached);
+ }
+
+ break;
+ }
+
+ case SHADOW_MSG_OUT_AUDIO_OUT_SAMPLES_ID:
+ {
+ const SHADOW_MSG_OUT_AUDIO_OUT_SAMPLES* msg =
+ (const SHADOW_MSG_OUT_AUDIO_OUT_SAMPLES*)message->wParam;
+
+ WINPR_ASSERT(msg);
+
+ if (client->activated && client->rdpsnd && client->rdpsnd->Activated)
+ {
+ client->rdpsnd->src_format = msg->audio_format;
+ IFCALL(client->rdpsnd->SendSamples, client->rdpsnd, msg->buf, msg->nFrames,
+ msg->wTimestamp);
+ }
+
+ break;
+ }
+
+ case SHADOW_MSG_OUT_AUDIO_OUT_VOLUME_ID:
+ {
+ const SHADOW_MSG_OUT_AUDIO_OUT_VOLUME* msg =
+ (const SHADOW_MSG_OUT_AUDIO_OUT_VOLUME*)message->wParam;
+
+ if (client->activated && client->rdpsnd && client->rdpsnd->Activated)
+ {
+ IFCALL(client->rdpsnd->SetVolume, client->rdpsnd, msg->left, msg->right);
+ }
+
+ break;
+ }
+
+ default:
+ WLog_ERR(TAG, "Unknown message id: %" PRIu32 "", message->id);
+ break;
+ }
+
+ shadow_client_free_queued_message(message);
+ return 1;
+}
+
+static DWORD WINAPI shadow_client_thread(LPVOID arg)
+{
+ rdpShadowClient* client = (rdpShadowClient*)arg;
+ BOOL rc = FALSE;
+ DWORD status = 0;
+ wMessage message = { 0 };
+ wMessage pointerPositionMsg = { 0 };
+ wMessage pointerAlphaMsg = { 0 };
+ wMessage audioVolumeMsg = { 0 };
+ HANDLE ChannelEvent = 0;
+ void* UpdateSubscriber = NULL;
+ HANDLE UpdateEvent = 0;
+ freerdp_peer* peer = NULL;
+ rdpContext* context = NULL;
+ rdpSettings* settings = NULL;
+ rdpShadowServer* server = NULL;
+ rdpShadowSubsystem* subsystem = NULL;
+ wMessageQueue* MsgQueue = NULL;
+ /* This should only be visited in client thread */
+ SHADOW_GFX_STATUS gfxstatus = { 0 };
+ rdpUpdate* update = NULL;
+
+ WINPR_ASSERT(client);
+
+ MsgQueue = client->MsgQueue;
+ WINPR_ASSERT(MsgQueue);
+
+ server = client->server;
+ WINPR_ASSERT(server);
+ subsystem = server->subsystem;
+ context = (rdpContext*)client;
+ peer = context->peer;
+ WINPR_ASSERT(peer);
+ WINPR_ASSERT(peer->context);
+
+ settings = peer->context->settings;
+ WINPR_ASSERT(settings);
+
+ peer->Capabilities = shadow_client_capabilities;
+ peer->PostConnect = shadow_client_post_connect;
+ peer->Activate = shadow_client_activate;
+ peer->Logon = shadow_client_logon;
+ shadow_input_register_callbacks(peer->context->input);
+
+ rc = peer->Initialize(peer);
+ if (!rc)
+ goto out;
+
+ update = peer->context->update;
+ WINPR_ASSERT(update);
+
+ update->RefreshRect = shadow_client_refresh_rect;
+ update->SuppressOutput = shadow_client_suppress_output;
+ update->SurfaceFrameAcknowledge = shadow_client_surface_frame_acknowledge;
+
+ if ((!client->vcm) || (!subsystem->updateEvent))
+ goto out;
+
+ UpdateSubscriber = shadow_multiclient_get_subscriber(subsystem->updateEvent);
+
+ if (!UpdateSubscriber)
+ goto out;
+
+ UpdateEvent = shadow_multiclient_getevent(UpdateSubscriber);
+ WINPR_ASSERT(UpdateEvent);
+
+ ChannelEvent = WTSVirtualChannelManagerGetEventHandle(client->vcm);
+ WINPR_ASSERT(ChannelEvent);
+
+ rc = freerdp_settings_set_bool(settings, FreeRDP_UnicodeInput, TRUE);
+ WINPR_ASSERT(rc);
+ rc = freerdp_settings_set_bool(settings, FreeRDP_HasHorizontalWheel, TRUE);
+ WINPR_ASSERT(rc);
+ rc = freerdp_settings_set_bool(settings, FreeRDP_HasExtendedMouseEvent, TRUE);
+ WINPR_ASSERT(rc);
+ rc = freerdp_settings_set_bool(settings, FreeRDP_SupportMonitorLayoutPdu, TRUE);
+ WINPR_ASSERT(rc);
+ while (1)
+ {
+ HANDLE events[MAXIMUM_WAIT_OBJECTS] = { 0 };
+ DWORD nCount = 0;
+ events[nCount++] = UpdateEvent;
+ {
+ DWORD tmp = peer->GetEventHandles(peer, &events[nCount], 64 - nCount);
+
+ if (tmp == 0)
+ {
+ WLog_ERR(TAG, "Failed to get FreeRDP transport event handles");
+ goto fail;
+ }
+
+ nCount += tmp;
+ }
+ events[nCount++] = ChannelEvent;
+ events[nCount++] = MessageQueue_Event(MsgQueue);
+
+ HANDLE gfxevent = rdpgfx_server_get_event_handle(client->rdpgfx);
+
+ if (gfxevent)
+ events[nCount++] = gfxevent;
+
+ status = WaitForMultipleObjects(nCount, events, FALSE, INFINITE);
+
+ if (status == WAIT_FAILED)
+ goto fail;
+
+ if (WaitForSingleObject(UpdateEvent, 0) == WAIT_OBJECT_0)
+ {
+ /* 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)
+ {
+ /* Send screen update or resize to this client */
+
+ /* Check resize */
+ if (shadow_client_recalc_desktop_size(client))
+ {
+ /* Screen size changed, do resize */
+ if (!shadow_client_send_resize(client, &gfxstatus))
+ {
+ WLog_ERR(TAG, "Failed to send resize message");
+ break;
+ }
+ }
+ else
+ {
+ /* Send frame */
+ if (!shadow_client_send_surface_update(client, &gfxstatus))
+ {
+ WLog_ERR(TAG, "Failed to send surface update");
+ break;
+ }
+ }
+ }
+ else
+ {
+ /* Our client don't receive graphic updates. Just save the invalid region */
+ if (!shadow_client_no_surface_update(client, &gfxstatus))
+ {
+ WLog_ERR(TAG, "Failed to handle surface update");
+ break;
+ }
+ }
+
+ /*
+ * The return value of shadow_multiclient_consume is whether or not
+ * the subscriber really consumes the event. It's not cared currently.
+ */
+ (void)shadow_multiclient_consume(UpdateSubscriber);
+ }
+
+ WINPR_ASSERT(peer->CheckFileDescriptor);
+ if (!peer->CheckFileDescriptor(peer))
+ {
+ WLog_ERR(TAG, "Failed to check FreeRDP file descriptor");
+ goto fail;
+ }
+
+ if (client->activated &&
+ WTSVirtualChannelManagerIsChannelJoined(client->vcm, DRDYNVC_SVC_CHANNEL_NAME))
+ {
+ switch (WTSVirtualChannelManagerGetDrdynvcState(client->vcm))
+ {
+ /* Dynamic channel status may have been changed after processing */
+ case DRDYNVC_STATE_NONE:
+
+ /* Call this routine to Initialize drdynvc channel */
+ if (!WTSVirtualChannelManagerCheckFileDescriptor(client->vcm))
+ {
+ WLog_ERR(TAG, "Failed to initialize drdynvc channel");
+ goto fail;
+ }
+
+ break;
+
+ case DRDYNVC_STATE_READY:
+#if defined(CHANNEL_AUDIN_SERVER)
+ if (client->audin && !IFCALLRESULT(TRUE, client->audin->IsOpen, client->audin))
+ {
+ if (!IFCALLRESULT(FALSE, client->audin->Open, client->audin))
+ {
+ WLog_ERR(TAG, "Failed to initialize audin channel");
+ goto fail;
+ }
+ }
+#endif
+
+ /* Init RDPGFX dynamic channel */
+ if (freerdp_settings_get_bool(settings, FreeRDP_SupportGraphicsPipeline) &&
+ client->rdpgfx && !gfxstatus.gfxOpened)
+ {
+ client->rdpgfx->FrameAcknowledge = shadow_client_rdpgfx_frame_acknowledge;
+ client->rdpgfx->CapsAdvertise = shadow_client_rdpgfx_caps_advertise;
+
+ if (!client->rdpgfx->Open(client->rdpgfx))
+ {
+ WLog_WARN(TAG, "Failed to open GraphicsPipeline");
+ if (!freerdp_settings_set_bool(settings,
+ FreeRDP_SupportGraphicsPipeline, FALSE))
+ goto fail;
+ }
+ else
+ {
+ gfxstatus.gfxOpened = TRUE;
+ WLog_INFO(TAG, "Gfx Pipeline Opened");
+ }
+ }
+
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ if (WaitForSingleObject(ChannelEvent, 0) == WAIT_OBJECT_0)
+ {
+ if (!WTSVirtualChannelManagerCheckFileDescriptor(client->vcm))
+ {
+ WLog_ERR(TAG, "WTSVirtualChannelManagerCheckFileDescriptor failure");
+ goto fail;
+ }
+ }
+
+ if (gfxevent)
+ {
+ if (WaitForSingleObject(gfxevent, 0) == WAIT_OBJECT_0)
+ {
+ rdpgfx_server_handle_messages(client->rdpgfx);
+ }
+ }
+
+ if (WaitForSingleObject(MessageQueue_Event(MsgQueue), 0) == WAIT_OBJECT_0)
+ {
+ /* Drain messages. Pointer update could be accumulated. */
+ pointerPositionMsg.id = 0;
+ pointerPositionMsg.Free = NULL;
+ pointerAlphaMsg.id = 0;
+ pointerAlphaMsg.Free = NULL;
+ audioVolumeMsg.id = 0;
+ audioVolumeMsg.Free = NULL;
+
+ while (MessageQueue_Peek(MsgQueue, &message, TRUE))
+ {
+ if (message.id == WMQ_QUIT)
+ {
+ break;
+ }
+
+ switch (message.id)
+ {
+ case SHADOW_MSG_OUT_POINTER_POSITION_UPDATE_ID:
+ /* Abandon previous message */
+ shadow_client_free_queued_message(&pointerPositionMsg);
+ pointerPositionMsg = message;
+ break;
+
+ case SHADOW_MSG_OUT_POINTER_ALPHA_UPDATE_ID:
+ /* Abandon previous message */
+ shadow_client_free_queued_message(&pointerAlphaMsg);
+ pointerAlphaMsg = message;
+ break;
+
+ case SHADOW_MSG_OUT_AUDIO_OUT_VOLUME_ID:
+ /* Abandon previous message */
+ shadow_client_free_queued_message(&audioVolumeMsg);
+ audioVolumeMsg = message;
+ break;
+
+ default:
+ shadow_client_subsystem_process_message(client, &message);
+ break;
+ }
+ }
+
+ if (message.id == WMQ_QUIT)
+ {
+ /* Release stored message */
+ shadow_client_free_queued_message(&pointerPositionMsg);
+ shadow_client_free_queued_message(&pointerAlphaMsg);
+ shadow_client_free_queued_message(&audioVolumeMsg);
+ goto fail;
+ }
+ else
+ {
+ /* Process accumulated messages if needed */
+ if (pointerPositionMsg.id)
+ {
+ shadow_client_subsystem_process_message(client, &pointerPositionMsg);
+ }
+
+ if (pointerAlphaMsg.id)
+ {
+ shadow_client_subsystem_process_message(client, &pointerAlphaMsg);
+ }
+
+ if (audioVolumeMsg.id)
+ {
+ shadow_client_subsystem_process_message(client, &audioVolumeMsg);
+ }
+ }
+ }
+ }
+
+fail:
+
+ /* Free channels early because we establish channels in post connect */
+#if defined(CHANNEL_AUDIN_SERVER)
+ if (client->audin && !IFCALLRESULT(TRUE, client->audin->IsOpen, client->audin))
+ {
+ if (!IFCALLRESULT(FALSE, client->audin->Close, client->audin))
+ {
+ WLog_WARN(TAG, "AUDIN shutdown failure!");
+ }
+ }
+#endif
+
+ if (gfxstatus.gfxOpened)
+ {
+ if (gfxstatus.gfxSurfaceCreated)
+ {
+ if (!shadow_client_rdpgfx_release_surface(client))
+ WLog_WARN(TAG, "GFX release surface failure!");
+ }
+
+ WINPR_ASSERT(client->rdpgfx);
+ WINPR_ASSERT(client->rdpgfx->Close);
+ rc = client->rdpgfx->Close(client->rdpgfx);
+ WINPR_ASSERT(rc);
+ }
+
+ shadow_client_channels_free(client);
+
+ if (UpdateSubscriber)
+ {
+ shadow_multiclient_release_subscriber(UpdateSubscriber);
+ UpdateSubscriber = NULL;
+ }
+
+ if (peer->connected && subsystem->ClientDisconnect)
+ {
+ subsystem->ClientDisconnect(subsystem, client);
+ }
+
+out:
+ WINPR_ASSERT(peer->Disconnect);
+ peer->Disconnect(peer);
+ freerdp_peer_context_free(peer);
+ freerdp_peer_free(peer);
+ ExitThread(0);
+ return 0;
+}
+
+BOOL shadow_client_accepted(freerdp_listener* listener, freerdp_peer* peer)
+{
+ rdpShadowClient* client = NULL;
+ rdpShadowServer* server = NULL;
+
+ if (!listener || !peer)
+ return FALSE;
+
+ server = (rdpShadowServer*)listener->info;
+ WINPR_ASSERT(server);
+
+ peer->ContextExtra = (void*)server;
+ peer->ContextSize = sizeof(rdpShadowClient);
+ peer->ContextNew = shadow_client_context_new;
+ peer->ContextFree = shadow_client_context_free;
+
+ if (!freerdp_peer_context_new_ex(peer, server->settings))
+ return FALSE;
+
+ client = (rdpShadowClient*)peer->context;
+ WINPR_ASSERT(client);
+
+ if (!(client->thread = CreateThread(NULL, 0, shadow_client_thread, client, 0, NULL)))
+ {
+ freerdp_peer_context_free(peer);
+ return FALSE;
+ }
+ else
+ {
+ /* Close the thread handle to make it detached. */
+ CloseHandle(client->thread);
+ client->thread = NULL;
+ }
+
+ return TRUE;
+}
+
+static void shadow_msg_out_addref(wMessage* message)
+{
+ SHADOW_MSG_OUT* msg = NULL;
+
+ WINPR_ASSERT(message);
+ msg = (SHADOW_MSG_OUT*)message->wParam;
+ WINPR_ASSERT(msg);
+
+ InterlockedIncrement(&(msg->refCount));
+}
+
+static void shadow_msg_out_release(wMessage* message)
+{
+ SHADOW_MSG_OUT* msg = NULL;
+
+ WINPR_ASSERT(message);
+ msg = (SHADOW_MSG_OUT*)message->wParam;
+ WINPR_ASSERT(msg);
+
+ if (InterlockedDecrement(&(msg->refCount)) <= 0)
+ {
+ IFCALL(msg->Free, message->id, msg);
+ }
+}
+
+static BOOL shadow_client_dispatch_msg(rdpShadowClient* client, wMessage* message)
+{
+ if (!client || !message)
+ return FALSE;
+
+ /* Add reference when it is posted */
+ shadow_msg_out_addref(message);
+
+ WINPR_ASSERT(client->MsgQueue);
+ if (MessageQueue_Dispatch(client->MsgQueue, message))
+ return TRUE;
+ else
+ {
+ /* Release the reference since post failed */
+ shadow_msg_out_release(message);
+ return FALSE;
+ }
+}
+
+BOOL shadow_client_post_msg(rdpShadowClient* client, void* context, UINT32 type,
+ SHADOW_MSG_OUT* msg, void* lParam)
+{
+ wMessage message = { 0 };
+ message.context = context;
+ message.id = type;
+ message.wParam = (void*)msg;
+ message.lParam = lParam;
+ message.Free = shadow_msg_out_release;
+ return shadow_client_dispatch_msg(client, &message);
+}
+
+int shadow_client_boardcast_msg(rdpShadowServer* server, void* context, UINT32 type,
+ SHADOW_MSG_OUT* msg, void* lParam)
+{
+ wMessage message = { 0 };
+ rdpShadowClient* client = NULL;
+ int count = 0;
+
+ WINPR_ASSERT(server);
+ WINPR_ASSERT(msg);
+
+ message.context = context;
+ message.id = type;
+ message.wParam = (void*)msg;
+ message.lParam = lParam;
+ message.Free = shadow_msg_out_release;
+ /* First add reference as we reference it in this function.
+ * Therefore it would not be free'ed during post. */
+ shadow_msg_out_addref(&message);
+
+ WINPR_ASSERT(server->clients);
+ ArrayList_Lock(server->clients);
+
+ for (size_t index = 0; index < ArrayList_Count(server->clients); index++)
+ {
+ client = (rdpShadowClient*)ArrayList_GetItem(server->clients, index);
+
+ if (shadow_client_dispatch_msg(client, &message))
+ {
+ count++;
+ }
+ }
+
+ ArrayList_Unlock(server->clients);
+ /* Release the reference for this function */
+ shadow_msg_out_release(&message);
+ return count;
+}
+
+int shadow_client_boardcast_quit(rdpShadowServer* server, int nExitCode)
+{
+ wMessageQueue* queue = NULL;
+ int count = 0;
+
+ WINPR_ASSERT(server);
+ WINPR_ASSERT(server->clients);
+
+ ArrayList_Lock(server->clients);
+
+ for (size_t index = 0; index < ArrayList_Count(server->clients); index++)
+ {
+ queue = ((rdpShadowClient*)ArrayList_GetItem(server->clients, index))->MsgQueue;
+
+ if (MessageQueue_PostQuit(queue, nExitCode))
+ {
+ count++;
+ }
+ }
+
+ ArrayList_Unlock(server->clients);
+ return count;
+}
diff --git a/server/shadow/shadow_client.h b/server/shadow/shadow_client.h
new file mode 100644
index 0000000..9334924
--- /dev/null
+++ b/server/shadow/shadow_client.h
@@ -0,0 +1,35 @@
+/**
+ * FreeRDP: A Remote Desktop Protocol Implementation
+ *
+ * Copyright 2014 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.
+ */
+
+#ifndef FREERDP_SERVER_SHADOW_CLIENT_H
+#define FREERDP_SERVER_SHADOW_CLIENT_H
+
+#include <freerdp/server/shadow.h>
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+ BOOL shadow_client_accepted(freerdp_listener* instance, freerdp_peer* client);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* FREERDP_SERVER_SHADOW_CLIENT_H */
diff --git a/server/shadow/shadow_encoder.c b/server/shadow/shadow_encoder.c
new file mode 100644
index 0000000..fc1747c
--- /dev/null
+++ b/server/shadow/shadow_encoder.c
@@ -0,0 +1,520 @@
+/**
+ * FreeRDP: A Remote Desktop Protocol Implementation
+ *
+ * Copyright 2014 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.
+ */
+
+#include <freerdp/config.h>
+
+#include <winpr/assert.h>
+
+#include "shadow.h"
+
+#include "shadow_encoder.h"
+
+#include <freerdp/log.h>
+#define TAG CLIENT_TAG("shadow")
+
+UINT32 shadow_encoder_preferred_fps(rdpShadowEncoder* encoder)
+{
+ /* Return preferred fps calculated according to the last
+ * sent frame id and last client-acknowledged frame id.
+ */
+ return encoder->fps;
+}
+
+UINT32 shadow_encoder_inflight_frames(rdpShadowEncoder* encoder)
+{
+ /* Return inflight frame count.
+ * If queueDepth is SUSPEND_FRAME_ACKNOWLEDGEMENT, count = 0
+ * Otherwise, calculate count =
+ * <last sent frame id> - <last client-acknowledged frame id>
+ * Note: This function is exported so that subsystem could
+ * implement its own strategy to tune fps.
+ */
+ return (encoder->queueDepth == SUSPEND_FRAME_ACKNOWLEDGEMENT)
+ ? 0
+ : encoder->frameId - encoder->lastAckframeId;
+}
+
+UINT32 shadow_encoder_create_frame_id(rdpShadowEncoder* encoder)
+{
+ UINT32 frameId = 0;
+ UINT32 inFlightFrames = shadow_encoder_inflight_frames(encoder);
+
+ /*
+ * Calculate preferred fps according to how much frames are
+ * in-progress. Note that it only works when subsytem implementation
+ * calls shadow_encoder_preferred_fps and takes the suggestion.
+ */
+ if (inFlightFrames > 1)
+ {
+ encoder->fps = (100 / (inFlightFrames + 1) * encoder->maxFps) / 100;
+ }
+ else
+ {
+ encoder->fps += 2;
+
+ if (encoder->fps > encoder->maxFps)
+ encoder->fps = encoder->maxFps;
+ }
+
+ if (encoder->fps < 1)
+ encoder->fps = 1;
+
+ frameId = ++encoder->frameId;
+ return frameId;
+}
+
+static int shadow_encoder_init_grid(rdpShadowEncoder* encoder)
+{
+ UINT32 tileSize = 0;
+ UINT32 tileCount = 0;
+ encoder->gridWidth = ((encoder->width + (encoder->maxTileWidth - 1)) / encoder->maxTileWidth);
+ encoder->gridHeight =
+ ((encoder->height + (encoder->maxTileHeight - 1)) / encoder->maxTileHeight);
+ tileSize = encoder->maxTileWidth * encoder->maxTileHeight * 4;
+ tileCount = encoder->gridWidth * encoder->gridHeight;
+ encoder->gridBuffer = (BYTE*)calloc(tileSize, tileCount);
+
+ if (!encoder->gridBuffer)
+ return -1;
+
+ encoder->grid = (BYTE**)calloc(tileCount, sizeof(BYTE*));
+
+ if (!encoder->grid)
+ return -1;
+
+ for (UINT32 i = 0; i < encoder->gridHeight; i++)
+ {
+ for (UINT32 j = 0; j < encoder->gridWidth; j++)
+ {
+ UINT32 k = (i * encoder->gridWidth) + j;
+ encoder->grid[k] = &(encoder->gridBuffer[k * tileSize]);
+ }
+ }
+
+ return 0;
+}
+
+static int shadow_encoder_uninit_grid(rdpShadowEncoder* encoder)
+{
+ if (encoder->gridBuffer)
+ {
+ free(encoder->gridBuffer);
+ encoder->gridBuffer = NULL;
+ }
+
+ if (encoder->grid)
+ {
+ free(encoder->grid);
+ encoder->grid = NULL;
+ }
+
+ encoder->gridWidth = 0;
+ encoder->gridHeight = 0;
+ return 0;
+}
+
+static int shadow_encoder_init_rfx(rdpShadowEncoder* encoder)
+{
+ if (!encoder->rfx)
+ encoder->rfx = rfx_context_new_ex(
+ TRUE, freerdp_settings_get_uint32(encoder->server->settings, FreeRDP_ThreadingFlags));
+
+ if (!encoder->rfx)
+ goto fail;
+
+ if (!rfx_context_reset(encoder->rfx, encoder->width, encoder->height))
+ goto fail;
+
+ rfx_context_set_mode(encoder->rfx, encoder->server->rfxMode);
+ rfx_context_set_pixel_format(encoder->rfx, PIXEL_FORMAT_BGRX32);
+ encoder->codecs |= FREERDP_CODEC_REMOTEFX;
+ return 1;
+fail:
+ rfx_context_free(encoder->rfx);
+ return -1;
+}
+
+static int shadow_encoder_init_nsc(rdpShadowEncoder* encoder)
+{
+ rdpContext* context = (rdpContext*)encoder->client;
+ rdpSettings* settings = context->settings;
+
+ if (!encoder->nsc)
+ encoder->nsc = nsc_context_new();
+
+ if (!encoder->nsc)
+ goto fail;
+
+ if (!nsc_context_reset(encoder->nsc, encoder->width, encoder->height))
+ goto fail;
+
+ if (!nsc_context_set_parameters(
+ encoder->nsc, NSC_COLOR_LOSS_LEVEL,
+ freerdp_settings_get_uint32(settings, FreeRDP_NSCodecColorLossLevel)))
+ goto fail;
+ if (!nsc_context_set_parameters(
+ encoder->nsc, NSC_ALLOW_SUBSAMPLING,
+ freerdp_settings_get_bool(settings, FreeRDP_NSCodecAllowSubsampling)))
+ goto fail;
+ if (!nsc_context_set_parameters(
+ encoder->nsc, NSC_DYNAMIC_COLOR_FIDELITY,
+ !freerdp_settings_get_bool(settings, FreeRDP_NSCodecAllowDynamicColorFidelity)))
+ goto fail;
+ if (!nsc_context_set_parameters(encoder->nsc, NSC_COLOR_FORMAT, PIXEL_FORMAT_BGRX32))
+ goto fail;
+ encoder->codecs |= FREERDP_CODEC_NSCODEC;
+ return 1;
+fail:
+ nsc_context_free(encoder->nsc);
+ return -1;
+}
+
+static int shadow_encoder_init_planar(rdpShadowEncoder* encoder)
+{
+ DWORD planarFlags = 0;
+ rdpContext* context = (rdpContext*)encoder->client;
+ rdpSettings* settings = context->settings;
+
+ if (freerdp_settings_get_bool(settings, FreeRDP_DrawAllowSkipAlpha))
+ planarFlags |= PLANAR_FORMAT_HEADER_NA;
+
+ planarFlags |= PLANAR_FORMAT_HEADER_RLE;
+
+ if (!encoder->planar)
+ {
+ encoder->planar = freerdp_bitmap_planar_context_new(planarFlags, encoder->maxTileWidth,
+ encoder->maxTileHeight);
+ }
+
+ if (!encoder->planar)
+ goto fail;
+
+ if (!freerdp_bitmap_planar_context_reset(encoder->planar, encoder->maxTileWidth,
+ encoder->maxTileHeight))
+ goto fail;
+
+ encoder->codecs |= FREERDP_CODEC_PLANAR;
+ return 1;
+fail:
+ freerdp_bitmap_planar_context_free(encoder->planar);
+ return -1;
+}
+
+static int shadow_encoder_init_interleaved(rdpShadowEncoder* encoder)
+{
+ if (!encoder->interleaved)
+ encoder->interleaved = bitmap_interleaved_context_new(TRUE);
+
+ if (!encoder->interleaved)
+ 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;
+}
+
+static 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;
+
+ if (!h264_context_set_option(encoder->h264, H264_CONTEXT_OPTION_RATECONTROL,
+ encoder->server->h264RateControlMode))
+ goto fail;
+ if (!h264_context_set_option(encoder->h264, H264_CONTEXT_OPTION_BITRATE,
+ encoder->server->h264BitRate))
+ goto fail;
+ if (!h264_context_set_option(encoder->h264, H264_CONTEXT_OPTION_FRAMERATE,
+ encoder->server->h264FrameRate))
+ goto fail;
+ if (!h264_context_set_option(encoder->h264, H264_CONTEXT_OPTION_QP, encoder->server->h264QP))
+ goto fail;
+
+ encoder->codecs |= FREERDP_CODEC_AVC420 | FREERDP_CODEC_AVC444;
+ return 1;
+fail:
+ h264_context_free(encoder->h264);
+ return -1;
+}
+
+static int shadow_encoder_init_progressive(rdpShadowEncoder* encoder)
+{
+ WINPR_ASSERT(encoder);
+ if (!encoder->progressive)
+ encoder->progressive = progressive_context_new(TRUE);
+
+ if (!encoder->progressive)
+ goto fail;
+
+ if (!progressive_context_reset(encoder->progressive))
+ goto fail;
+
+ encoder->codecs |= FREERDP_CODEC_PROGRESSIVE;
+ return 1;
+fail:
+ progressive_context_free(encoder->progressive);
+ return -1;
+}
+
+static int shadow_encoder_init(rdpShadowEncoder* encoder)
+{
+ encoder->width = encoder->server->screen->width;
+ encoder->height = encoder->server->screen->height;
+ encoder->maxTileWidth = 64;
+ encoder->maxTileHeight = 64;
+ shadow_encoder_init_grid(encoder);
+
+ if (!encoder->bs)
+ encoder->bs = Stream_New(NULL, encoder->maxTileWidth * encoder->maxTileHeight * 4ULL);
+
+ if (!encoder->bs)
+ return -1;
+
+ return 1;
+}
+
+static int shadow_encoder_uninit_rfx(rdpShadowEncoder* encoder)
+{
+ if (encoder->rfx)
+ {
+ rfx_context_free(encoder->rfx);
+ encoder->rfx = NULL;
+ }
+
+ encoder->codecs &= (UINT32)~FREERDP_CODEC_REMOTEFX;
+ return 1;
+}
+
+static int shadow_encoder_uninit_nsc(rdpShadowEncoder* encoder)
+{
+ if (encoder->nsc)
+ {
+ nsc_context_free(encoder->nsc);
+ encoder->nsc = NULL;
+ }
+
+ encoder->codecs &= (UINT32)~FREERDP_CODEC_NSCODEC;
+ return 1;
+}
+
+static int shadow_encoder_uninit_planar(rdpShadowEncoder* encoder)
+{
+ if (encoder->planar)
+ {
+ freerdp_bitmap_planar_context_free(encoder->planar);
+ encoder->planar = NULL;
+ }
+
+ encoder->codecs &= (UINT32)~FREERDP_CODEC_PLANAR;
+ return 1;
+}
+
+static int shadow_encoder_uninit_interleaved(rdpShadowEncoder* encoder)
+{
+ if (encoder->interleaved)
+ {
+ bitmap_interleaved_context_free(encoder->interleaved);
+ encoder->interleaved = NULL;
+ }
+
+ encoder->codecs &= (UINT32)~FREERDP_CODEC_INTERLEAVED;
+ return 1;
+}
+
+static int shadow_encoder_uninit_h264(rdpShadowEncoder* encoder)
+{
+ if (encoder->h264)
+ {
+ h264_context_free(encoder->h264);
+ encoder->h264 = NULL;
+ }
+
+ encoder->codecs &= (UINT32) ~(FREERDP_CODEC_AVC420 | FREERDP_CODEC_AVC444);
+ return 1;
+}
+
+static int shadow_encoder_uninit_progressive(rdpShadowEncoder* encoder)
+{
+ WINPR_ASSERT(encoder);
+ if (encoder->progressive)
+ {
+ progressive_context_free(encoder->progressive);
+ encoder->progressive = NULL;
+ }
+
+ encoder->codecs &= (UINT32)~FREERDP_CODEC_PROGRESSIVE;
+ return 1;
+}
+
+static int shadow_encoder_uninit(rdpShadowEncoder* encoder)
+{
+ shadow_encoder_uninit_grid(encoder);
+
+ if (encoder->bs)
+ {
+ Stream_Free(encoder->bs, TRUE);
+ encoder->bs = NULL;
+ }
+
+ shadow_encoder_uninit_rfx(encoder);
+
+ shadow_encoder_uninit_nsc(encoder);
+
+ shadow_encoder_uninit_planar(encoder);
+
+ shadow_encoder_uninit_interleaved(encoder);
+ shadow_encoder_uninit_h264(encoder);
+
+ shadow_encoder_uninit_progressive(encoder);
+
+ return 1;
+}
+
+int shadow_encoder_reset(rdpShadowEncoder* encoder)
+{
+ int status = 0;
+ UINT32 codecs = encoder->codecs;
+ rdpContext* context = (rdpContext*)encoder->client;
+ rdpSettings* settings = context->settings;
+ status = shadow_encoder_uninit(encoder);
+
+ if (status < 0)
+ return -1;
+
+ status = shadow_encoder_init(encoder);
+
+ if (status < 0)
+ return -1;
+
+ status = shadow_encoder_prepare(encoder, codecs);
+
+ if (status < 0)
+ return -1;
+
+ encoder->fps = 16;
+ encoder->maxFps = 32;
+ encoder->frameId = 0;
+ encoder->lastAckframeId = 0;
+ encoder->frameAck = freerdp_settings_get_bool(settings, FreeRDP_SurfaceFrameMarkerEnabled);
+ return 1;
+}
+
+int shadow_encoder_prepare(rdpShadowEncoder* encoder, UINT32 codecs)
+{
+ int status = 0;
+
+ if ((codecs & FREERDP_CODEC_REMOTEFX) && !(encoder->codecs & FREERDP_CODEC_REMOTEFX))
+ {
+ WLog_DBG(TAG, "initializing RemoteFX encoder");
+ status = shadow_encoder_init_rfx(encoder);
+
+ if (status < 0)
+ return -1;
+ }
+
+ if ((codecs & FREERDP_CODEC_NSCODEC) && !(encoder->codecs & FREERDP_CODEC_NSCODEC))
+ {
+ WLog_DBG(TAG, "initializing NSCodec encoder");
+ status = shadow_encoder_init_nsc(encoder);
+
+ if (status < 0)
+ return -1;
+ }
+
+ if ((codecs & FREERDP_CODEC_PLANAR) && !(encoder->codecs & FREERDP_CODEC_PLANAR))
+ {
+ WLog_DBG(TAG, "initializing planar bitmap encoder");
+ status = shadow_encoder_init_planar(encoder);
+
+ if (status < 0)
+ return -1;
+ }
+
+ if ((codecs & FREERDP_CODEC_INTERLEAVED) && !(encoder->codecs & FREERDP_CODEC_INTERLEAVED))
+ {
+ WLog_DBG(TAG, "initializing interleaved bitmap encoder");
+ status = shadow_encoder_init_interleaved(encoder);
+
+ if (status < 0)
+ return -1;
+ }
+
+ if ((codecs & (FREERDP_CODEC_AVC420 | FREERDP_CODEC_AVC444)) &&
+ !(encoder->codecs & (FREERDP_CODEC_AVC420 | FREERDP_CODEC_AVC444)))
+ {
+ WLog_DBG(TAG, "initializing H.264 encoder");
+ status = shadow_encoder_init_h264(encoder);
+
+ if (status < 0)
+ return -1;
+ }
+
+ if ((codecs & FREERDP_CODEC_PROGRESSIVE) && !(encoder->codecs & FREERDP_CODEC_PROGRESSIVE))
+ {
+ WLog_DBG(TAG, "initializing progressive encoder");
+ status = shadow_encoder_init_progressive(encoder);
+
+ if (status < 0)
+ return -1;
+ }
+
+ return 1;
+}
+
+rdpShadowEncoder* shadow_encoder_new(rdpShadowClient* client)
+{
+ rdpShadowEncoder* encoder = NULL;
+ rdpShadowServer* server = client->server;
+ encoder = (rdpShadowEncoder*)calloc(1, sizeof(rdpShadowEncoder));
+
+ if (!encoder)
+ return NULL;
+
+ encoder->client = client;
+ encoder->server = server;
+ encoder->fps = 16;
+ encoder->maxFps = 32;
+
+ if (shadow_encoder_init(encoder) < 0)
+ {
+ free(encoder);
+ return NULL;
+ }
+
+ return encoder;
+}
+
+void shadow_encoder_free(rdpShadowEncoder* encoder)
+{
+ if (!encoder)
+ return;
+
+ shadow_encoder_uninit(encoder);
+ free(encoder);
+}
diff --git a/server/shadow/shadow_encoder.h b/server/shadow/shadow_encoder.h
new file mode 100644
index 0000000..dfe00f3
--- /dev/null
+++ b/server/shadow/shadow_encoder.h
@@ -0,0 +1,81 @@
+/**
+ * FreeRDP: A Remote Desktop Protocol Implementation
+ *
+ * Copyright 2014 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.
+ */
+
+#ifndef FREERDP_SERVER_SHADOW_ENCODER_H
+#define FREERDP_SERVER_SHADOW_ENCODER_H
+
+#include <winpr/crt.h>
+#include <winpr/stream.h>
+
+#include <freerdp/freerdp.h>
+#include <freerdp/codecs.h>
+
+#include <freerdp/server/shadow.h>
+
+struct rdp_shadow_encoder
+{
+ rdpShadowClient* client;
+ rdpShadowServer* server;
+
+ UINT32 width;
+ UINT32 height;
+ UINT32 codecs;
+
+ BYTE** grid;
+ UINT32 gridWidth;
+ UINT32 gridHeight;
+ BYTE* gridBuffer;
+ UINT32 maxTileWidth;
+ UINT32 maxTileHeight;
+
+ wStream* bs;
+
+ RFX_CONTEXT* rfx;
+ NSC_CONTEXT* nsc;
+ BITMAP_PLANAR_CONTEXT* planar;
+ BITMAP_INTERLEAVED_CONTEXT* interleaved;
+ H264_CONTEXT* h264;
+ PROGRESSIVE_CONTEXT* progressive;
+
+ UINT32 fps;
+ UINT32 maxFps;
+ BOOL frameAck;
+ UINT32 frameId;
+ UINT32 lastAckframeId;
+ UINT32 queueDepth;
+};
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+ int shadow_encoder_reset(rdpShadowEncoder* encoder);
+ int shadow_encoder_prepare(rdpShadowEncoder* encoder, UINT32 codecs);
+ UINT32 shadow_encoder_create_frame_id(rdpShadowEncoder* encoder);
+
+ void shadow_encoder_free(rdpShadowEncoder* encoder);
+
+ WINPR_ATTR_MALLOC(shadow_encoder_free, 1)
+ rdpShadowEncoder* shadow_encoder_new(rdpShadowClient* client);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* FREERDP_SERVER_SHADOW_ENCODER_H */
diff --git a/server/shadow/shadow_encomsp.c b/server/shadow/shadow_encomsp.c
new file mode 100644
index 0000000..8a3a468
--- /dev/null
+++ b/server/shadow/shadow_encomsp.c
@@ -0,0 +1,129 @@
+/**
+ * FreeRDP: A Remote Desktop Protocol Implementation
+ *
+ * Copyright 2014 Marc-Andre Moreau <marcandre.moreau@gmail.com>
+ * Copyright 2015 Thincast Technologies GmbH
+ * Copyright 2015 DI (FH) Martin Haimberger <martin.haimberger@thincast.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.
+ */
+
+#include <freerdp/config.h>
+
+#include <freerdp/log.h>
+#include "shadow.h"
+
+#include "shadow_encomsp.h"
+
+#define TAG SERVER_TAG("shadow")
+
+/**
+ * Function description
+ *
+ * @return 0 on success, otherwise a Win32 error code
+ */
+static UINT
+encomsp_change_participant_control_level(EncomspServerContext* context,
+ ENCOMSP_CHANGE_PARTICIPANT_CONTROL_LEVEL_PDU* pdu)
+{
+ BOOL inLobby = 0;
+ BOOL mayView = 0;
+ BOOL mayInteract = 0;
+ rdpShadowClient* client = (rdpShadowClient*)context->custom;
+
+ WLog_INFO(TAG,
+ "ChangeParticipantControlLevel: ParticipantId: %" PRIu32 " Flags: 0x%04" PRIX16 "",
+ pdu->ParticipantId, pdu->Flags);
+
+ mayView = (pdu->Flags & ENCOMSP_MAY_VIEW) ? TRUE : FALSE;
+ mayInteract = (pdu->Flags & ENCOMSP_MAY_INTERACT) ? TRUE : FALSE;
+
+ if (mayInteract && !mayView)
+ mayView = TRUE; /* may interact implies may view */
+
+ if (mayInteract)
+ {
+ if (!client->mayInteract)
+ {
+ /* request interact + view */
+ client->mayInteract = TRUE;
+ client->mayView = TRUE;
+ }
+ }
+ else if (mayView)
+ {
+ if (client->mayInteract)
+ {
+ /* release interact */
+ client->mayInteract = FALSE;
+ }
+ else if (!client->mayView)
+ {
+ /* request view */
+ client->mayView = TRUE;
+ }
+ }
+ else
+ {
+ if (client->mayInteract)
+ {
+ /* release interact + view */
+ client->mayView = FALSE;
+ client->mayInteract = FALSE;
+ }
+ else if (client->mayView)
+ {
+ /* release view */
+ client->mayView = FALSE;
+ client->mayInteract = FALSE;
+ }
+ }
+
+ inLobby = client->mayView ? FALSE : TRUE;
+
+ if (inLobby != client->inLobby)
+ {
+ shadow_encoder_reset(client->encoder);
+ client->inLobby = inLobby;
+ }
+
+ return CHANNEL_RC_OK;
+}
+
+int shadow_client_encomsp_init(rdpShadowClient* client)
+{
+ EncomspServerContext* encomsp = NULL;
+
+ encomsp = client->encomsp = encomsp_server_context_new(client->vcm);
+
+ encomsp->rdpcontext = &client->context;
+
+ encomsp->custom = (void*)client;
+
+ encomsp->ChangeParticipantControlLevel = encomsp_change_participant_control_level;
+
+ if (client->encomsp)
+ client->encomsp->Start(client->encomsp);
+
+ return 1;
+}
+
+void shadow_client_encomsp_uninit(rdpShadowClient* client)
+{
+ if (client->encomsp)
+ {
+ client->encomsp->Stop(client->encomsp);
+ encomsp_server_context_free(client->encomsp);
+ client->encomsp = NULL;
+ }
+}
diff --git a/server/shadow/shadow_encomsp.h b/server/shadow/shadow_encomsp.h
new file mode 100644
index 0000000..6562e25
--- /dev/null
+++ b/server/shadow/shadow_encomsp.h
@@ -0,0 +1,39 @@
+/**
+ * FreeRDP: A Remote Desktop Protocol Implementation
+ *
+ * Copyright 2014 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.
+ */
+
+#ifndef FREERDP_SERVER_SHADOW_ENCOMSP_H
+#define FREERDP_SERVER_SHADOW_ENCOMSP_H
+
+#include <freerdp/server/shadow.h>
+
+#include <winpr/crt.h>
+#include <winpr/synch.h>
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+ int shadow_client_encomsp_init(rdpShadowClient* client);
+ void shadow_client_encomsp_uninit(rdpShadowClient* client);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* FREERDP_SERVER_SHADOW_ENCOMSP_H */
diff --git a/server/shadow/shadow_input.c b/server/shadow/shadow_input.c
new file mode 100644
index 0000000..97268b8
--- /dev/null
+++ b/server/shadow/shadow_input.c
@@ -0,0 +1,114 @@
+/**
+ * FreeRDP: A Remote Desktop Protocol Implementation
+ *
+ * Copyright 2014 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.
+ */
+
+#include <freerdp/config.h>
+
+#include "shadow.h"
+
+static BOOL shadow_input_synchronize_event(rdpInput* input, UINT32 flags)
+{
+ rdpShadowClient* client = (rdpShadowClient*)input->context;
+ rdpShadowSubsystem* subsystem = client->server->subsystem;
+
+ if (!client->mayInteract)
+ return TRUE;
+
+ return IFCALLRESULT(TRUE, subsystem->SynchronizeEvent, subsystem, client, flags);
+}
+
+static BOOL shadow_input_keyboard_event(rdpInput* input, UINT16 flags, UINT8 code)
+{
+ rdpShadowClient* client = (rdpShadowClient*)input->context;
+ rdpShadowSubsystem* subsystem = client->server->subsystem;
+
+ if (!client->mayInteract)
+ return TRUE;
+
+ return IFCALLRESULT(TRUE, subsystem->KeyboardEvent, subsystem, client, flags, code);
+}
+
+static BOOL shadow_input_unicode_keyboard_event(rdpInput* input, UINT16 flags, UINT16 code)
+{
+ rdpShadowClient* client = (rdpShadowClient*)input->context;
+ rdpShadowSubsystem* subsystem = client->server->subsystem;
+
+ if (!client->mayInteract)
+ return TRUE;
+
+ return IFCALLRESULT(TRUE, subsystem->UnicodeKeyboardEvent, subsystem, client, flags, code);
+}
+
+static BOOL shadow_input_mouse_event(rdpInput* input, UINT16 flags, UINT16 x, UINT16 y)
+{
+ rdpShadowClient* client = (rdpShadowClient*)input->context;
+ rdpShadowSubsystem* subsystem = client->server->subsystem;
+
+ if (client->server->shareSubRect)
+ {
+ x += client->server->subRect.left;
+ y += client->server->subRect.top;
+ }
+
+ if (!(flags & PTR_FLAGS_WHEEL))
+ {
+ client->pointerX = x;
+ client->pointerY = y;
+
+ if ((client->pointerX == subsystem->pointerX) && (client->pointerY == subsystem->pointerY))
+ {
+ flags &= ~PTR_FLAGS_MOVE;
+
+ if (!(flags & (PTR_FLAGS_BUTTON1 | PTR_FLAGS_BUTTON2 | PTR_FLAGS_BUTTON3)))
+ return TRUE;
+ }
+ }
+
+ if (!client->mayInteract)
+ return TRUE;
+
+ return IFCALLRESULT(TRUE, subsystem->MouseEvent, subsystem, client, flags, x, y);
+}
+
+static BOOL shadow_input_extended_mouse_event(rdpInput* input, UINT16 flags, UINT16 x, UINT16 y)
+{
+ rdpShadowClient* client = (rdpShadowClient*)input->context;
+ rdpShadowSubsystem* subsystem = client->server->subsystem;
+
+ if (client->server->shareSubRect)
+ {
+ x += client->server->subRect.left;
+ y += client->server->subRect.top;
+ }
+
+ client->pointerX = x;
+ client->pointerY = y;
+
+ if (!client->mayInteract)
+ return TRUE;
+
+ return IFCALLRESULT(TRUE, subsystem->ExtendedMouseEvent, subsystem, client, flags, x, y);
+}
+
+void shadow_input_register_callbacks(rdpInput* input)
+{
+ input->SynchronizeEvent = shadow_input_synchronize_event;
+ input->KeyboardEvent = shadow_input_keyboard_event;
+ input->UnicodeKeyboardEvent = shadow_input_unicode_keyboard_event;
+ input->MouseEvent = shadow_input_mouse_event;
+ input->ExtendedMouseEvent = shadow_input_extended_mouse_event;
+}
diff --git a/server/shadow/shadow_input.h b/server/shadow/shadow_input.h
new file mode 100644
index 0000000..8bde31c
--- /dev/null
+++ b/server/shadow/shadow_input.h
@@ -0,0 +1,35 @@
+/**
+ * FreeRDP: A Remote Desktop Protocol Implementation
+ *
+ * Copyright 2014 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.
+ */
+
+#ifndef FREERDP_SERVER_SHADOW_INPUT_H
+#define FREERDP_SERVER_SHADOW_INPUT_H
+
+#include <freerdp/server/shadow.h>
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+ void shadow_input_register_callbacks(rdpInput* input);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* FREERDP_SERVER_SHADOW_INPUT_H */
diff --git a/server/shadow/shadow_lobby.c b/server/shadow/shadow_lobby.c
new file mode 100644
index 0000000..8a9fade
--- /dev/null
+++ b/server/shadow/shadow_lobby.c
@@ -0,0 +1,85 @@
+/**
+ * FreeRDP: A Remote Desktop Protocol Implementation
+ *
+ * Copyright 2014 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.
+ */
+
+#include <freerdp/config.h>
+
+#include <winpr/assert.h>
+#include <rdtk/rdtk.h>
+
+#include "shadow.h"
+
+#include "shadow_lobby.h"
+
+BOOL shadow_client_init_lobby(rdpShadowServer* server)
+{
+ int width = 0;
+ int height = 0;
+ rdtkEngine* engine = NULL;
+ rdtkSurface* surface = NULL;
+ RECTANGLE_16 invalidRect;
+ rdpShadowSurface* lobby = server->lobby;
+
+ if (!lobby)
+ return FALSE;
+
+ if (!(engine = rdtk_engine_new()))
+ {
+ return FALSE;
+ }
+
+ if (!(surface =
+ rdtk_surface_new(engine, lobby->data, lobby->width, lobby->height, lobby->scanline)))
+ {
+ rdtk_engine_free(engine);
+ return FALSE;
+ }
+
+ invalidRect.left = 0;
+ invalidRect.top = 0;
+ WINPR_ASSERT(lobby->width <= UINT16_MAX);
+ WINPR_ASSERT(lobby->height <= UINT16_MAX);
+ invalidRect.right = (UINT16)lobby->width;
+ invalidRect.bottom = (UINT16)lobby->height;
+ if (server->shareSubRect)
+ {
+ /* If we have shared sub rect setting, only fill shared rect */
+ rectangles_intersection(&invalidRect, &(server->subRect), &invalidRect);
+ }
+
+ width = invalidRect.right - invalidRect.left;
+ height = invalidRect.bottom - invalidRect.top;
+ WINPR_ASSERT(width <= UINT16_MAX);
+ WINPR_ASSERT(width >= 0);
+ WINPR_ASSERT(height <= UINT16_MAX);
+ WINPR_ASSERT(height >= 0);
+ rdtk_surface_fill(surface, invalidRect.left, invalidRect.top, (UINT16)width, (UINT16)height,
+ 0x3BB9FF);
+
+ rdtk_label_draw(surface, invalidRect.left, invalidRect.top, (UINT16)width, (UINT16)height, NULL,
+ "Welcome", 0, 0);
+ // rdtk_button_draw(surface, 16, 64, 128, 32, NULL, "button");
+ // rdtk_text_field_draw(surface, 16, 128, 128, 32, NULL, "text field");
+
+ rdtk_surface_free(surface);
+
+ rdtk_engine_free(engine);
+
+ region16_union_rect(&(lobby->invalidRegion), &(lobby->invalidRegion), &invalidRect);
+
+ return TRUE;
+}
diff --git a/server/shadow/shadow_lobby.h b/server/shadow/shadow_lobby.h
new file mode 100644
index 0000000..37ad9cf
--- /dev/null
+++ b/server/shadow/shadow_lobby.h
@@ -0,0 +1,40 @@
+/**
+ * FreeRDP: A Remote Desktop Protocol Implementation
+ *
+ * Copyright 2014 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.
+ */
+
+#ifndef FREERDP_SERVER_SHADOW_LOBBY_H
+#define FREERDP_SERVER_SHADOW_LOBBY_H
+
+#include <freerdp/server/shadow.h>
+
+#include <winpr/crt.h>
+#include <winpr/synch.h>
+
+#include <rdtk/rdtk.h>
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+ BOOL shadow_client_init_lobby(rdpShadowServer* server);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* FREERDP_SERVER_SHADOW_LOBBY_H */
diff --git a/server/shadow/shadow_mcevent.c b/server/shadow/shadow_mcevent.c
new file mode 100644
index 0000000..4609cee
--- /dev/null
+++ b/server/shadow/shadow_mcevent.c
@@ -0,0 +1,355 @@
+/**
+ * FreeRDP: A Remote Desktop Protocol Implementation
+ *
+ * Copyright 2015 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.
+ */
+
+#include <freerdp/config.h>
+
+#include <winpr/assert.h>
+#include <freerdp/log.h>
+#include "shadow.h"
+
+#define TAG SERVER_TAG("shadow.mcevent")
+
+struct rdp_shadow_multiclient_event
+{
+ HANDLE event; /* Kickoff event */
+ HANDLE barrierEvent; /* Represents that all clients have consumed event */
+ HANDLE doneEvent; /* Event handling finished. Server could continue */
+ wArrayList* subscribers;
+ CRITICAL_SECTION lock;
+ int consuming;
+ int waiting;
+
+ /* For debug */
+ int eventid;
+};
+
+struct rdp_shadow_multiclient_subscriber
+{
+ rdpShadowMultiClientEvent* ref;
+ BOOL pleaseHandle; /* Indicate if server expects my handling in this turn */
+};
+
+rdpShadowMultiClientEvent* shadow_multiclient_new(void)
+{
+ rdpShadowMultiClientEvent* event =
+ (rdpShadowMultiClientEvent*)calloc(1, sizeof(rdpShadowMultiClientEvent));
+ if (!event)
+ goto out_error;
+
+ event->event = CreateEvent(NULL, TRUE, FALSE, NULL);
+ if (!event->event)
+ goto out_free;
+
+ event->barrierEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
+ if (!event->barrierEvent)
+ goto out_free_event;
+
+ event->doneEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
+ if (!event->doneEvent)
+ goto out_free_barrierEvent;
+
+ event->subscribers = ArrayList_New(TRUE);
+ if (!event->subscribers)
+ goto out_free_doneEvent;
+
+ if (!InitializeCriticalSectionAndSpinCount(&(event->lock), 4000))
+ goto out_free_subscribers;
+
+ event->consuming = 0;
+ event->waiting = 0;
+ event->eventid = 0;
+ SetEvent(event->doneEvent);
+ return event;
+
+out_free_subscribers:
+ ArrayList_Free(event->subscribers);
+out_free_doneEvent:
+ CloseHandle(event->doneEvent);
+out_free_barrierEvent:
+ CloseHandle(event->barrierEvent);
+out_free_event:
+ CloseHandle(event->event);
+out_free:
+ free(event);
+out_error:
+ return (rdpShadowMultiClientEvent*)NULL;
+}
+
+void shadow_multiclient_free(rdpShadowMultiClientEvent* event)
+{
+ if (!event)
+ return;
+
+ DeleteCriticalSection(&(event->lock));
+
+ ArrayList_Free(event->subscribers);
+ CloseHandle(event->doneEvent);
+ CloseHandle(event->barrierEvent);
+ CloseHandle(event->event);
+ free(event);
+
+ return;
+}
+
+static void _Publish(rdpShadowMultiClientEvent* event)
+{
+ wArrayList* subscribers = NULL;
+ struct rdp_shadow_multiclient_subscriber* subscriber = NULL;
+
+ subscribers = event->subscribers;
+
+ WINPR_ASSERT(event->consuming == 0);
+
+ /* Count subscribing clients */
+ ArrayList_Lock(subscribers);
+ for (size_t i = 0; i < ArrayList_Count(subscribers); i++)
+ {
+ subscriber = (struct rdp_shadow_multiclient_subscriber*)ArrayList_GetItem(subscribers, i);
+ /* Set flag to subscriber: I acknowledge and please handle */
+ subscriber->pleaseHandle = TRUE;
+ event->consuming++;
+ }
+ ArrayList_Unlock(subscribers);
+
+ if (event->consuming > 0)
+ {
+ event->eventid = (event->eventid & 0xff) + 1;
+ WLog_VRB(TAG, "Server published event %d. %d clients.\n", event->eventid, event->consuming);
+ ResetEvent(event->doneEvent);
+ SetEvent(event->event);
+ }
+
+ return;
+}
+
+static void _WaitForSubscribers(rdpShadowMultiClientEvent* event)
+{
+ if (event->consuming > 0)
+ {
+ /* Wait for clients done */
+ WLog_VRB(TAG, "Server wait event %d. %d clients.\n", event->eventid, event->consuming);
+ LeaveCriticalSection(&(event->lock));
+ WaitForSingleObject(event->doneEvent, INFINITE);
+ EnterCriticalSection(&(event->lock));
+ WLog_VRB(TAG, "Server quit event %d. %d clients.\n", event->eventid, event->consuming);
+ }
+
+ /* Last subscriber should have already reset the event */
+ WINPR_ASSERT(WaitForSingleObject(event->event, 0) != WAIT_OBJECT_0);
+
+ return;
+}
+
+void shadow_multiclient_publish(rdpShadowMultiClientEvent* event)
+{
+ if (!event)
+ return;
+
+ EnterCriticalSection(&(event->lock));
+ _Publish(event);
+ LeaveCriticalSection(&(event->lock));
+
+ return;
+}
+void shadow_multiclient_wait(rdpShadowMultiClientEvent* event)
+{
+ if (!event)
+ return;
+
+ EnterCriticalSection(&(event->lock));
+ _WaitForSubscribers(event);
+ LeaveCriticalSection(&(event->lock));
+
+ return;
+}
+void shadow_multiclient_publish_and_wait(rdpShadowMultiClientEvent* event)
+{
+ if (!event)
+ return;
+
+ EnterCriticalSection(&(event->lock));
+ _Publish(event);
+ _WaitForSubscribers(event);
+ LeaveCriticalSection(&(event->lock));
+
+ return;
+}
+
+static BOOL _Consume(struct rdp_shadow_multiclient_subscriber* subscriber, BOOL wait)
+{
+ rdpShadowMultiClientEvent* event = subscriber->ref;
+ BOOL ret = FALSE;
+
+ if (WaitForSingleObject(event->event, 0) == WAIT_OBJECT_0 && subscriber->pleaseHandle)
+ {
+ /* Consume my share. Server is waiting for us */
+ event->consuming--;
+ ret = TRUE;
+ }
+
+ WINPR_ASSERT(event->consuming >= 0);
+
+ if (event->consuming == 0)
+ {
+ /* Last client reset event before notify clients to continue */
+ ResetEvent(event->event);
+
+ if (event->waiting > 0)
+ {
+ /* Notify other clients to continue */
+ SetEvent(event->barrierEvent);
+ }
+ else
+ {
+ /* Only one client. Notify server directly */
+ SetEvent(event->doneEvent);
+ }
+ }
+ else /* (event->consuming > 0) */
+ {
+ if (wait)
+ {
+ /*
+ * This client need to wait. That means the client will
+ * continue waiting for other clients to finish.
+ * The last client should reset barrierEvent.
+ */
+ event->waiting++;
+ LeaveCriticalSection(&(event->lock));
+ WaitForSingleObject(event->barrierEvent, INFINITE);
+ EnterCriticalSection(&(event->lock));
+ event->waiting--;
+ if (event->waiting == 0)
+ {
+ /*
+ * This is last client waiting for barrierEvent.
+ * We can now discard barrierEvent and notify
+ * server to continue.
+ */
+ ResetEvent(event->barrierEvent);
+ SetEvent(event->doneEvent);
+ }
+ }
+ }
+
+ return ret;
+}
+
+void* shadow_multiclient_get_subscriber(rdpShadowMultiClientEvent* event)
+{
+ struct rdp_shadow_multiclient_subscriber* subscriber = NULL;
+
+ if (!event)
+ return NULL;
+
+ EnterCriticalSection(&(event->lock));
+
+ subscriber = (struct rdp_shadow_multiclient_subscriber*)calloc(
+ 1, sizeof(struct rdp_shadow_multiclient_subscriber));
+ if (!subscriber)
+ goto out_error;
+
+ subscriber->ref = event;
+ subscriber->pleaseHandle = FALSE;
+
+ if (!ArrayList_Append(event->subscribers, subscriber))
+ goto out_free;
+
+ WLog_VRB(TAG, "Get subscriber %p. Wait event %d. %d clients.\n", (void*)subscriber,
+ event->eventid, event->consuming);
+ (void)_Consume(subscriber, TRUE);
+ WLog_VRB(TAG, "Get subscriber %p. Quit event %d. %d clients.\n", (void*)subscriber,
+ event->eventid, event->consuming);
+
+ LeaveCriticalSection(&(event->lock));
+
+ return subscriber;
+
+out_free:
+ free(subscriber);
+out_error:
+ LeaveCriticalSection(&(event->lock));
+ return NULL;
+}
+
+/*
+ * Consume my share and release my register
+ * If we have update event and pleaseHandle flag
+ * We need to consume. Anyway we need to clear
+ * pleaseHandle flag
+ */
+void shadow_multiclient_release_subscriber(void* subscriber)
+{
+ struct rdp_shadow_multiclient_subscriber* s = NULL;
+ rdpShadowMultiClientEvent* event = NULL;
+
+ if (!subscriber)
+ return;
+
+ s = (struct rdp_shadow_multiclient_subscriber*)subscriber;
+ event = s->ref;
+
+ EnterCriticalSection(&(event->lock));
+
+ WLog_VRB(TAG, "Release Subscriber %p. Drop event %d. %d clients.\n", subscriber, event->eventid,
+ event->consuming);
+ (void)_Consume(s, FALSE);
+ WLog_VRB(TAG, "Release Subscriber %p. Quit event %d. %d clients.\n", subscriber, event->eventid,
+ event->consuming);
+
+ ArrayList_Remove(event->subscribers, subscriber);
+
+ LeaveCriticalSection(&(event->lock));
+
+ free(subscriber);
+
+ return;
+}
+
+BOOL shadow_multiclient_consume(void* subscriber)
+{
+ struct rdp_shadow_multiclient_subscriber* s = NULL;
+ rdpShadowMultiClientEvent* event = NULL;
+ BOOL ret = FALSE;
+
+ if (!subscriber)
+ return ret;
+
+ s = (struct rdp_shadow_multiclient_subscriber*)subscriber;
+ event = s->ref;
+
+ EnterCriticalSection(&(event->lock));
+
+ WLog_VRB(TAG, "Subscriber %p wait event %d. %d clients.\n", subscriber, event->eventid,
+ event->consuming);
+ ret = _Consume(s, TRUE);
+ WLog_VRB(TAG, "Subscriber %p quit event %d. %d clients.\n", subscriber, event->eventid,
+ event->consuming);
+
+ LeaveCriticalSection(&(event->lock));
+
+ return ret;
+}
+
+HANDLE shadow_multiclient_getevent(void* subscriber)
+{
+ if (!subscriber)
+ return (HANDLE)NULL;
+
+ return ((struct rdp_shadow_multiclient_subscriber*)subscriber)->ref->event;
+}
diff --git a/server/shadow/shadow_mcevent.h b/server/shadow/shadow_mcevent.h
new file mode 100644
index 0000000..c78b920
--- /dev/null
+++ b/server/shadow/shadow_mcevent.h
@@ -0,0 +1,56 @@
+/**
+ * FreeRDP: A Remote Desktop Protocol Implementation
+ *
+ * Copyright 2015 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_SERVER_SHADOW_MCEVENT_H
+#define FREERDP_SERVER_SHADOW_MCEVENT_H
+
+#include <freerdp/server/shadow.h>
+
+#include <winpr/crt.h>
+#include <winpr/synch.h>
+#include <winpr/collections.h>
+
+/*
+ * This file implemented a model that an event is consumed
+ * by multiple clients. All clients should wait others before continue
+ * Server should wait for all clients before continue
+ */
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+ void shadow_multiclient_free(rdpShadowMultiClientEvent* event);
+
+ WINPR_ATTR_MALLOC(shadow_multiclient_free, 1)
+ rdpShadowMultiClientEvent* shadow_multiclient_new(void);
+
+ void shadow_multiclient_publish(rdpShadowMultiClientEvent* event);
+ void shadow_multiclient_wait(rdpShadowMultiClientEvent* event);
+ void shadow_multiclient_publish_and_wait(rdpShadowMultiClientEvent* event);
+ void* shadow_multiclient_get_subscriber(rdpShadowMultiClientEvent* event);
+ void shadow_multiclient_release_subscriber(void* subscriber);
+ BOOL shadow_multiclient_consume(void* subscriber);
+ HANDLE shadow_multiclient_getevent(void* subscriber);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* FREERDP_SERVER_SHADOW_MCEVENT_H */
diff --git a/server/shadow/shadow_rdpgfx.c b/server/shadow/shadow_rdpgfx.c
new file mode 100644
index 0000000..16bb236
--- /dev/null
+++ b/server/shadow/shadow_rdpgfx.c
@@ -0,0 +1,55 @@
+/**
+ * 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.
+ */
+
+#include <freerdp/config.h>
+
+#include <freerdp/log.h>
+#include "shadow.h"
+
+#include "shadow_rdpgfx.h"
+
+#define TAG SERVER_TAG("shadow")
+
+int shadow_client_rdpgfx_init(rdpShadowClient* client)
+{
+ WINPR_ASSERT(client);
+
+ RdpgfxServerContext* rdpgfx = client->rdpgfx = rdpgfx_server_context_new(client->vcm);
+ if (!rdpgfx)
+ {
+ return 0;
+ }
+
+ rdpgfx->rdpcontext = &client->context;
+
+ rdpgfx->custom = client;
+
+ if (!IFCALLRESULT(CHANNEL_RC_OK, rdpgfx->Initialize, rdpgfx, TRUE))
+ return -1;
+
+ 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
index 0000000..ce81376
--- /dev/null
+++ b/server/shadow/shadow_rdpgfx.h
@@ -0,0 +1,39 @@
+/**
+ * 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_SERVER_SHADOW_RDPGFX_H
+#define FREERDP_SERVER_SHADOW_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_SERVER_SHADOW_RDPGFX_H */
diff --git a/server/shadow/shadow_rdpsnd.c b/server/shadow/shadow_rdpsnd.c
new file mode 100644
index 0000000..16899e4
--- /dev/null
+++ b/server/shadow/shadow_rdpsnd.c
@@ -0,0 +1,87 @@
+/**
+ * FreeRDP: A Remote Desktop Protocol Implementation
+ *
+ * Copyright 2015 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.
+ */
+
+#include <freerdp/config.h>
+
+#include <winpr/crt.h>
+#include <freerdp/log.h>
+#include <freerdp/codec/dsp.h>
+#include <freerdp/server/server-common.h>
+
+#include "shadow.h"
+
+#include "shadow_rdpsnd.h"
+
+#define TAG SERVER_TAG("shadow")
+
+static void rdpsnd_activated(RdpsndServerContext* context)
+{
+ for (size_t i = 0; i < context->num_client_formats; i++)
+ {
+ for (size_t j = 0; j < context->num_server_formats; j++)
+ {
+ if (audio_format_compatible(&context->server_formats[j], &context->client_formats[i]))
+ {
+ context->SelectFormat(context, i);
+ return;
+ }
+ }
+ }
+
+ WLog_ERR(TAG, "Could not agree on a audio format with the server\n");
+}
+
+int shadow_client_rdpsnd_init(rdpShadowClient* client)
+{
+ RdpsndServerContext* rdpsnd = NULL;
+ rdpsnd = client->rdpsnd = rdpsnd_server_context_new(client->vcm);
+
+ if (!rdpsnd)
+ {
+ return 0;
+ }
+
+ rdpsnd->data = client;
+
+ if (client->subsystem->rdpsndFormats)
+ {
+ rdpsnd->server_formats = client->subsystem->rdpsndFormats;
+ rdpsnd->num_server_formats = client->subsystem->nRdpsndFormats;
+ }
+ else
+ {
+ rdpsnd->num_server_formats = server_rdpsnd_get_formats(&rdpsnd->server_formats);
+ }
+
+ if (rdpsnd->num_server_formats > 0)
+ rdpsnd->src_format = &rdpsnd->server_formats[0];
+
+ rdpsnd->Activated = rdpsnd_activated;
+ rdpsnd->Initialize(rdpsnd, TRUE);
+ return 1;
+}
+
+void shadow_client_rdpsnd_uninit(rdpShadowClient* client)
+{
+ if (client->rdpsnd)
+ {
+ client->rdpsnd->Stop(client->rdpsnd);
+ rdpsnd_server_context_free(client->rdpsnd);
+ client->rdpsnd = NULL;
+ }
+}
diff --git a/server/shadow/shadow_rdpsnd.h b/server/shadow/shadow_rdpsnd.h
new file mode 100644
index 0000000..ae34ea3
--- /dev/null
+++ b/server/shadow/shadow_rdpsnd.h
@@ -0,0 +1,39 @@
+/**
+ * FreeRDP: A Remote Desktop Protocol Implementation
+ *
+ * Copyright 2015 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_SERVER_SHADOW_RDPSND_H
+#define FREERDP_SERVER_SHADOW_RDPSND_H
+
+#include <freerdp/server/shadow.h>
+
+#include <winpr/crt.h>
+#include <winpr/synch.h>
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+ int shadow_client_rdpsnd_init(rdpShadowClient* client);
+ void shadow_client_rdpsnd_uninit(rdpShadowClient* client);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* FREERDP_SERVER_SHADOW_RDPSND_H */
diff --git a/server/shadow/shadow_remdesk.c b/server/shadow/shadow_remdesk.c
new file mode 100644
index 0000000..19fb9f3
--- /dev/null
+++ b/server/shadow/shadow_remdesk.c
@@ -0,0 +1,50 @@
+/**
+ * FreeRDP: A Remote Desktop Protocol Implementation
+ *
+ * Copyright 2014 Marc-Andre Moreau <marcandre.moreau@gmail.com>
+ * Copyright 2015 Thincast Technologies GmbH
+ * Copyright 2015 DI (FH) Martin Haimberger <martin.haimberger@thincast.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.
+ */
+
+#include <freerdp/config.h>
+
+#include "shadow.h"
+
+#include "shadow_remdesk.h"
+
+int shadow_client_remdesk_init(rdpShadowClient* client)
+{
+ RemdeskServerContext* remdesk = NULL;
+
+ remdesk = client->remdesk = remdesk_server_context_new(client->vcm);
+ remdesk->rdpcontext = &client->context;
+
+ remdesk->custom = (void*)client;
+
+ if (client->remdesk)
+ client->remdesk->Start(client->remdesk);
+
+ return 1;
+}
+
+void shadow_client_remdesk_uninit(rdpShadowClient* client)
+{
+ if (client->remdesk)
+ {
+ client->remdesk->Stop(client->remdesk);
+ remdesk_server_context_free(client->remdesk);
+ client->remdesk = NULL;
+ }
+}
diff --git a/server/shadow/shadow_remdesk.h b/server/shadow/shadow_remdesk.h
new file mode 100644
index 0000000..88fdba0
--- /dev/null
+++ b/server/shadow/shadow_remdesk.h
@@ -0,0 +1,39 @@
+/**
+ * FreeRDP: A Remote Desktop Protocol Implementation
+ *
+ * Copyright 2014 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.
+ */
+
+#ifndef FREERDP_SERVER_SHADOW_REMDESK_H
+#define FREERDP_SERVER_SHADOW_REMDESK_H
+
+#include <freerdp/server/shadow.h>
+
+#include <winpr/crt.h>
+#include <winpr/synch.h>
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+ int shadow_client_remdesk_init(rdpShadowClient* client);
+ void shadow_client_remdesk_uninit(rdpShadowClient* client);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* FREERDP_SERVER_SHADOW_REMDESK_H */
diff --git a/server/shadow/shadow_screen.c b/server/shadow/shadow_screen.c
new file mode 100644
index 0000000..8a9e0b3
--- /dev/null
+++ b/server/shadow/shadow_screen.c
@@ -0,0 +1,163 @@
+/**
+ * FreeRDP: A Remote Desktop Protocol Implementation
+ *
+ * Copyright 2014 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.
+ */
+
+#include <freerdp/config.h>
+
+#include <winpr/assert.h>
+
+#include "shadow_surface.h"
+
+#include "shadow_screen.h"
+#include "shadow_lobby.h"
+
+rdpShadowScreen* shadow_screen_new(rdpShadowServer* server)
+{
+ WINPR_ASSERT(server);
+ WINPR_ASSERT(server->subsystem);
+
+ rdpShadowScreen* screen = (rdpShadowScreen*)calloc(1, sizeof(rdpShadowScreen));
+
+ if (!screen)
+ goto fail;
+
+ screen->server = server;
+ rdpShadowSubsystem* subsystem = server->subsystem;
+
+ if (!InitializeCriticalSectionAndSpinCount(&(screen->lock), 4000))
+ goto fail;
+
+ region16_init(&(screen->invalidRegion));
+
+ WINPR_ASSERT(subsystem->selectedMonitor < ARRAYSIZE(subsystem->monitors));
+ const MONITOR_DEF* primary = &(subsystem->monitors[subsystem->selectedMonitor]);
+ WINPR_ASSERT(primary);
+
+ INT64 x = primary->left;
+ INT64 y = primary->top;
+ INT64 width = primary->right - primary->left + 1;
+ INT64 height = primary->bottom - primary->top + 1;
+
+ WINPR_ASSERT(x >= 0);
+ WINPR_ASSERT(x <= UINT16_MAX);
+ WINPR_ASSERT(y >= 0);
+ WINPR_ASSERT(y <= UINT16_MAX);
+ WINPR_ASSERT(width >= 0);
+ WINPR_ASSERT(width <= UINT16_MAX);
+ WINPR_ASSERT(height >= 0);
+ WINPR_ASSERT(height <= UINT16_MAX);
+
+ screen->width = (UINT16)width;
+ screen->height = (UINT16)height;
+
+ screen->primary =
+ shadow_surface_new(server, (UINT16)x, (UINT16)y, (UINT16)width, (UINT16)height);
+
+ if (!screen->primary)
+ goto fail;
+
+ server->surface = screen->primary;
+
+ screen->lobby = shadow_surface_new(server, (UINT16)x, (UINT16)y, (UINT16)width, (UINT16)height);
+
+ if (!screen->lobby)
+ goto fail;
+
+ server->lobby = screen->lobby;
+
+ if (!shadow_client_init_lobby(server))
+ goto fail;
+
+ return screen;
+
+fail:
+ WINPR_PRAGMA_DIAG_PUSH
+ WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
+ shadow_screen_free(screen);
+ WINPR_PRAGMA_DIAG_POP
+
+ return NULL;
+}
+
+void shadow_screen_free(rdpShadowScreen* screen)
+{
+ if (!screen)
+ return;
+
+ DeleteCriticalSection(&(screen->lock));
+
+ region16_uninit(&(screen->invalidRegion));
+
+ if (screen->primary)
+ {
+ shadow_surface_free(screen->primary);
+ screen->primary = NULL;
+ }
+
+ if (screen->lobby)
+ {
+ shadow_surface_free(screen->lobby);
+ screen->lobby = NULL;
+ }
+
+ free(screen);
+}
+
+BOOL shadow_screen_resize(rdpShadowScreen* screen)
+{
+ int x = 0;
+ int y = 0;
+ int width = 0;
+ int height = 0;
+ MONITOR_DEF* primary = NULL;
+ rdpShadowSubsystem* subsystem = NULL;
+
+ if (!screen)
+ return FALSE;
+
+ subsystem = screen->server->subsystem;
+ primary = &(subsystem->monitors[subsystem->selectedMonitor]);
+
+ x = primary->left;
+ y = primary->top;
+ width = primary->right - primary->left + 1;
+ height = primary->bottom - primary->top + 1;
+
+ WINPR_ASSERT(x >= 0);
+ WINPR_ASSERT(x <= UINT16_MAX);
+ WINPR_ASSERT(y >= 0);
+ WINPR_ASSERT(y <= UINT16_MAX);
+ WINPR_ASSERT(width >= 0);
+ WINPR_ASSERT(width <= UINT16_MAX);
+ WINPR_ASSERT(height >= 0);
+ WINPR_ASSERT(height <= UINT16_MAX);
+ if (shadow_surface_resize(screen->primary, (UINT16)x, (UINT16)y, (UINT16)width,
+ (UINT16)height) &&
+ shadow_surface_resize(screen->lobby, (UINT16)x, (UINT16)y, (UINT16)width, (UINT16)height))
+ {
+ if (((UINT32)width != screen->width) || ((UINT32)height != screen->height))
+ {
+ /* screen size is changed. Store new size and reinit lobby */
+ screen->width = (UINT32)width;
+ screen->height = (UINT32)height;
+ shadow_client_init_lobby(screen->server);
+ }
+ return TRUE;
+ }
+
+ return FALSE;
+}
diff --git a/server/shadow/shadow_screen.h b/server/shadow/shadow_screen.h
new file mode 100644
index 0000000..a7bf789
--- /dev/null
+++ b/server/shadow/shadow_screen.h
@@ -0,0 +1,55 @@
+/**
+ * FreeRDP: A Remote Desktop Protocol Implementation
+ *
+ * Copyright 2014 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.
+ */
+
+#ifndef FREERDP_SERVER_SHADOW_SCREEN_H
+#define FREERDP_SERVER_SHADOW_SCREEN_H
+
+#include <freerdp/server/shadow.h>
+
+#include <winpr/crt.h>
+#include <winpr/synch.h>
+
+struct rdp_shadow_screen
+{
+ rdpShadowServer* server;
+
+ UINT32 width;
+ UINT32 height;
+
+ CRITICAL_SECTION lock;
+ REGION16 invalidRegion;
+
+ rdpShadowSurface* primary;
+ rdpShadowSurface* lobby;
+};
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+ void shadow_screen_free(rdpShadowScreen* screen);
+
+ WINPR_ATTR_MALLOC(shadow_screen_free, 1)
+ rdpShadowScreen* shadow_screen_new(rdpShadowServer* server);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* FREERDP_SERVER_SHADOW_SCREEN_H */
diff --git a/server/shadow/shadow_server.c b/server/shadow/shadow_server.c
new file mode 100644
index 0000000..067dcc6
--- /dev/null
+++ b/server/shadow/shadow_server.c
@@ -0,0 +1,1028 @@
+/**
+ * FreeRDP: A Remote Desktop Protocol Implementation
+ *
+ * Copyright 2014 Marc-Andre Moreau <marcandre.moreau@gmail.com>
+ * Copyright 2017 Armin Novak <armin.novak@thincast.com>
+ * Copyright 2017 Thincast Technologies GmbH
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <freerdp/config.h>
+
+#include <errno.h>
+
+#include <winpr/crt.h>
+#include <winpr/ssl.h>
+#include <winpr/path.h>
+#include <winpr/cmdline.h>
+#include <winpr/winsock.h>
+
+#include <freerdp/log.h>
+#include <freerdp/version.h>
+
+#include <winpr/tools/makecert.h>
+
+#ifndef _WIN32
+#include <sys/select.h>
+#include <signal.h>
+#endif
+
+#include "shadow.h"
+
+#define TAG SERVER_TAG("shadow")
+
+static const char bind_address[] = "bind-address,";
+
+static int shadow_server_print_command_line_help(int argc, char** argv,
+ COMMAND_LINE_ARGUMENT_A* largs)
+{
+ char* str = NULL;
+ size_t length = 0;
+ const COMMAND_LINE_ARGUMENT_A* arg = NULL;
+ if ((argc < 1) || !largs || !argv)
+ return -1;
+
+ printf("Usage: %s [options]\n", argv[0]);
+ printf("\n");
+ printf("Syntax:\n");
+ printf(" /flag (enables flag)\n");
+ printf(" /option:<value> (specifies option with value)\n");
+ printf(" +toggle -toggle (enables or disables toggle, where '/' is a synonym of '+')\n");
+ printf("\n");
+ arg = largs;
+
+ do
+ {
+ if (arg->Flags & COMMAND_LINE_VALUE_FLAG)
+ {
+ printf(" %s", "/");
+ printf("%-20s\n", arg->Name);
+ printf("\t%s\n", arg->Text);
+ }
+ else if ((arg->Flags & COMMAND_LINE_VALUE_REQUIRED) ||
+ (arg->Flags & COMMAND_LINE_VALUE_OPTIONAL))
+ {
+ printf(" %s", "/");
+
+ if (arg->Format)
+ {
+ length = (strlen(arg->Name) + strlen(arg->Format) + 2);
+ str = (char*)malloc(length + 1);
+
+ if (!str)
+ return -1;
+
+ sprintf_s(str, length + 1, "%s:%s", arg->Name, arg->Format);
+ printf("%-20s\n", str);
+ free(str);
+ }
+ else
+ {
+ printf("%-20s\n", arg->Name);
+ }
+
+ printf("\t%s\n", arg->Text);
+ }
+ else if (arg->Flags & COMMAND_LINE_VALUE_BOOL)
+ {
+ length = strlen(arg->Name) + 32;
+ str = (char*)malloc(length + 1);
+
+ if (!str)
+ return -1;
+
+ sprintf_s(str, length + 1, "%s (default:%s)", arg->Name, arg->Default ? "on" : "off");
+ printf(" %s", arg->Default ? "-" : "+");
+ printf("%-20s\n", str);
+ free(str);
+ printf("\t%s\n", arg->Text);
+ }
+ } while ((arg = CommandLineFindNextArgumentA(arg)) != NULL);
+
+ return 1;
+}
+
+int shadow_server_command_line_status_print(rdpShadowServer* server, int argc, char** argv,
+ int status, COMMAND_LINE_ARGUMENT_A* cargs)
+{
+ WINPR_UNUSED(server);
+
+ if (status == COMMAND_LINE_STATUS_PRINT_VERSION)
+ {
+ printf("FreeRDP version %s (git %s)\n", FREERDP_VERSION_FULL, FREERDP_GIT_REVISION);
+ return COMMAND_LINE_STATUS_PRINT_VERSION;
+ }
+ else if (status == COMMAND_LINE_STATUS_PRINT_BUILDCONFIG)
+ {
+ printf("%s\n", freerdp_get_build_config());
+ return COMMAND_LINE_STATUS_PRINT_BUILDCONFIG;
+ }
+ else if (status == COMMAND_LINE_STATUS_PRINT)
+ {
+ return COMMAND_LINE_STATUS_PRINT;
+ }
+ else if (status < 0)
+ {
+ if (shadow_server_print_command_line_help(argc, argv, cargs) < 0)
+ return -1;
+
+ return COMMAND_LINE_STATUS_PRINT_HELP;
+ }
+
+ return 1;
+}
+
+int shadow_server_parse_command_line(rdpShadowServer* server, int argc, char** argv,
+ COMMAND_LINE_ARGUMENT_A* cargs)
+{
+ int status = 0;
+ DWORD flags = 0;
+ const COMMAND_LINE_ARGUMENT_A* arg = NULL;
+ rdpSettings* settings = server->settings;
+
+ if ((argc < 2) || !argv || !cargs)
+ return 1;
+
+ CommandLineClearArgumentsA(cargs);
+ flags = COMMAND_LINE_SEPARATOR_COLON;
+ flags |= COMMAND_LINE_SIGIL_SLASH | COMMAND_LINE_SIGIL_PLUS_MINUS;
+ status = CommandLineParseArgumentsA(argc, argv, cargs, flags, server, NULL, NULL);
+
+ if (status < 0)
+ return status;
+
+ arg = cargs;
+ errno = 0;
+
+ do
+ {
+ if (!(arg->Flags & COMMAND_LINE_ARGUMENT_PRESENT))
+ continue;
+
+ CommandLineSwitchStart(arg) CommandLineSwitchCase(arg, "port")
+ {
+ long val = strtol(arg->Value, NULL, 0);
+
+ if ((errno != 0) || (val <= 0) || (val > UINT16_MAX))
+ return -1;
+
+ server->port = (DWORD)val;
+ }
+ CommandLineSwitchCase(arg, "ipc-socket")
+ {
+ /* /bind-address is incompatible */
+ if (server->ipcSocket)
+ return -1;
+ server->ipcSocket = _strdup(arg->Value);
+
+ if (!server->ipcSocket)
+ return -1;
+ }
+ CommandLineSwitchCase(arg, "bind-address")
+ {
+ int rc = 0;
+ size_t len = strlen(arg->Value) + sizeof(bind_address);
+ /* /ipc-socket is incompatible */
+ if (server->ipcSocket)
+ return -1;
+ server->ipcSocket = calloc(len, sizeof(CHAR));
+
+ if (!server->ipcSocket)
+ return -1;
+
+ rc = _snprintf(server->ipcSocket, len, "%s%s", bind_address, arg->Value);
+ if ((rc < 0) || ((size_t)rc != len - 1))
+ return -1;
+ }
+ CommandLineSwitchCase(arg, "may-view")
+ {
+ server->mayView = arg->Value ? TRUE : FALSE;
+ }
+ CommandLineSwitchCase(arg, "may-interact")
+ {
+ server->mayInteract = arg->Value ? TRUE : FALSE;
+ }
+ CommandLineSwitchCase(arg, "max-connections")
+ {
+ errno = 0;
+ unsigned long val = strtoul(arg->Value, NULL, 0);
+
+ if ((errno != 0) || (val > UINT32_MAX))
+ return -1;
+ server->maxClientsConnected = val;
+ }
+ CommandLineSwitchCase(arg, "rect")
+ {
+ char* p = NULL;
+ char* tok[4];
+ long x = -1;
+ long y = -1;
+ long w = -1;
+ long h = -1;
+ char* str = _strdup(arg->Value);
+
+ if (!str)
+ return -1;
+
+ tok[0] = p = str;
+ p = strchr(p + 1, ',');
+
+ if (!p)
+ {
+ free(str);
+ return -1;
+ }
+
+ *p++ = '\0';
+ tok[1] = p;
+ p = strchr(p + 1, ',');
+
+ if (!p)
+ {
+ free(str);
+ return -1;
+ }
+
+ *p++ = '\0';
+ tok[2] = p;
+ p = strchr(p + 1, ',');
+
+ if (!p)
+ {
+ free(str);
+ return -1;
+ }
+
+ *p++ = '\0';
+ tok[3] = p;
+ x = strtol(tok[0], NULL, 0);
+
+ if (errno != 0)
+ goto fail;
+
+ y = strtol(tok[1], NULL, 0);
+
+ if (errno != 0)
+ goto fail;
+
+ w = strtol(tok[2], NULL, 0);
+
+ if (errno != 0)
+ goto fail;
+
+ h = strtol(tok[3], NULL, 0);
+
+ if (errno != 0)
+ goto fail;
+
+ fail:
+ free(str);
+
+ if ((x < 0) || (y < 0) || (w < 1) || (h < 1) || (errno != 0))
+ return -1;
+
+ if ((x > UINT16_MAX) || (y > UINT16_MAX) || (x + w > UINT16_MAX) ||
+ (y + h > UINT16_MAX))
+ return -1;
+ server->subRect.left = (UINT16)x;
+ server->subRect.top = (UINT16)y;
+ server->subRect.right = (UINT16)(x + w);
+ server->subRect.bottom = (UINT16)(y + h);
+ server->shareSubRect = TRUE;
+ }
+ CommandLineSwitchCase(arg, "auth")
+ {
+ server->authentication = arg->Value ? TRUE : FALSE;
+ }
+ CommandLineSwitchCase(arg, "remote-guard")
+ {
+ if (!freerdp_settings_set_bool(settings, FreeRDP_RemoteCredentialGuard,
+ arg->Value ? TRUE : FALSE))
+ return COMMAND_LINE_ERROR;
+ }
+ CommandLineSwitchCase(arg, "sec")
+ {
+ if (strcmp("rdp", arg->Value) == 0) /* Standard RDP */
+ {
+ if (!freerdp_settings_set_bool(settings, FreeRDP_RdpSecurity, TRUE))
+ return COMMAND_LINE_ERROR;
+ if (!freerdp_settings_set_bool(settings, FreeRDP_TlsSecurity, FALSE))
+ return COMMAND_LINE_ERROR;
+ if (!freerdp_settings_set_bool(settings, FreeRDP_NlaSecurity, FALSE))
+ return COMMAND_LINE_ERROR;
+ if (!freerdp_settings_set_bool(settings, FreeRDP_ExtSecurity, FALSE))
+ return COMMAND_LINE_ERROR;
+ if (!freerdp_settings_set_bool(settings, FreeRDP_UseRdpSecurityLayer, TRUE))
+ return COMMAND_LINE_ERROR;
+ }
+ else if (strcmp("tls", arg->Value) == 0) /* TLS */
+ {
+ if (!freerdp_settings_set_bool(settings, FreeRDP_RdpSecurity, FALSE))
+ return COMMAND_LINE_ERROR;
+ if (!freerdp_settings_set_bool(settings, FreeRDP_TlsSecurity, TRUE))
+ return COMMAND_LINE_ERROR;
+ if (!freerdp_settings_set_bool(settings, FreeRDP_NlaSecurity, FALSE))
+ return COMMAND_LINE_ERROR;
+ if (!freerdp_settings_set_bool(settings, FreeRDP_ExtSecurity, FALSE))
+ return COMMAND_LINE_ERROR;
+ }
+ else if (strcmp("nla", arg->Value) == 0) /* NLA */
+ {
+ if (!freerdp_settings_set_bool(settings, FreeRDP_RdpSecurity, FALSE))
+ return COMMAND_LINE_ERROR;
+ if (!freerdp_settings_set_bool(settings, FreeRDP_TlsSecurity, FALSE))
+ return COMMAND_LINE_ERROR;
+ if (!freerdp_settings_set_bool(settings, FreeRDP_NlaSecurity, TRUE))
+ return COMMAND_LINE_ERROR;
+ if (!freerdp_settings_set_bool(settings, FreeRDP_ExtSecurity, FALSE))
+ return COMMAND_LINE_ERROR;
+ }
+ else if (strcmp("ext", arg->Value) == 0) /* NLA Extended */
+ {
+ if (!freerdp_settings_set_bool(settings, FreeRDP_RdpSecurity, FALSE))
+ return COMMAND_LINE_ERROR;
+ if (!freerdp_settings_set_bool(settings, FreeRDP_TlsSecurity, FALSE))
+ return COMMAND_LINE_ERROR;
+ if (!freerdp_settings_set_bool(settings, FreeRDP_NlaSecurity, FALSE))
+ return COMMAND_LINE_ERROR;
+ if (!freerdp_settings_set_bool(settings, FreeRDP_ExtSecurity, TRUE))
+ return COMMAND_LINE_ERROR;
+ }
+ else
+ {
+ WLog_ERR(TAG, "unknown protocol security: %s", arg->Value);
+ }
+ }
+ CommandLineSwitchCase(arg, "sec-rdp")
+ {
+ if (!freerdp_settings_set_bool(settings, FreeRDP_RdpSecurity,
+ arg->Value ? TRUE : FALSE))
+ return COMMAND_LINE_ERROR;
+ }
+ CommandLineSwitchCase(arg, "sec-tls")
+ {
+ if (!freerdp_settings_set_bool(settings, FreeRDP_TlsSecurity,
+ arg->Value ? TRUE : FALSE))
+ return COMMAND_LINE_ERROR;
+ }
+ CommandLineSwitchCase(arg, "sec-nla")
+ {
+ if (!freerdp_settings_set_bool(settings, FreeRDP_NlaSecurity,
+ arg->Value ? TRUE : FALSE))
+ return COMMAND_LINE_ERROR;
+ }
+ CommandLineSwitchCase(arg, "sec-ext")
+ {
+ if (!freerdp_settings_set_bool(settings, FreeRDP_ExtSecurity,
+ arg->Value ? TRUE : FALSE))
+ return COMMAND_LINE_ERROR;
+ }
+ CommandLineSwitchCase(arg, "sam-file")
+ {
+ freerdp_settings_set_string(settings, FreeRDP_NtlmSamFile, arg->Value);
+ }
+ CommandLineSwitchCase(arg, "log-level")
+ {
+ wLog* root = WLog_GetRoot();
+
+ if (!WLog_SetStringLogLevel(root, arg->Value))
+ return COMMAND_LINE_ERROR;
+ }
+ CommandLineSwitchCase(arg, "log-filters")
+ {
+ if (!WLog_AddStringLogFilters(arg->Value))
+ return COMMAND_LINE_ERROR;
+ }
+ CommandLineSwitchCase(arg, "gfx-progressive")
+ {
+ if (!freerdp_settings_set_bool(settings, FreeRDP_GfxProgressive,
+ arg->Value ? TRUE : FALSE))
+ return COMMAND_LINE_ERROR;
+ }
+ CommandLineSwitchCase(arg, "gfx-rfx")
+ {
+ if (!freerdp_settings_set_bool(settings, FreeRDP_RemoteFxCodec,
+ arg->Value ? TRUE : FALSE))
+ return COMMAND_LINE_ERROR;
+ }
+ CommandLineSwitchCase(arg, "gfx-planar")
+ {
+ if (!freerdp_settings_set_bool(settings, FreeRDP_GfxPlanar, arg->Value ? TRUE : FALSE))
+ return COMMAND_LINE_ERROR;
+ }
+ CommandLineSwitchCase(arg, "gfx-avc420")
+ {
+ if (!freerdp_settings_set_bool(settings, FreeRDP_GfxH264, arg->Value ? TRUE : FALSE))
+ return COMMAND_LINE_ERROR;
+ }
+ CommandLineSwitchCase(arg, "gfx-avc444")
+ {
+ if (!freerdp_settings_set_bool(settings, FreeRDP_GfxAVC444v2,
+ arg->Value ? TRUE : FALSE))
+ return COMMAND_LINE_ERROR;
+ if (!freerdp_settings_set_bool(settings, FreeRDP_GfxAVC444, arg->Value ? TRUE : FALSE))
+ return COMMAND_LINE_ERROR;
+ }
+ CommandLineSwitchCase(arg, "keytab")
+ {
+ if (!freerdp_settings_set_string(settings, FreeRDP_KerberosKeytab, arg->Value))
+ return COMMAND_LINE_ERROR;
+ }
+ CommandLineSwitchCase(arg, "ccache")
+ {
+ if (!freerdp_settings_set_string(settings, FreeRDP_KerberosCache, arg->Value))
+ return COMMAND_LINE_ERROR;
+ }
+ CommandLineSwitchCase(arg, "tls-secrets-file")
+ {
+ if (!freerdp_settings_set_string(settings, FreeRDP_TlsSecretsFile, arg->Value))
+ return COMMAND_LINE_ERROR;
+ }
+ CommandLineSwitchDefault(arg)
+ {
+ }
+ CommandLineSwitchEnd(arg)
+ } while ((arg = CommandLineFindNextArgumentA(arg)) != NULL);
+
+ arg = CommandLineFindArgumentA(cargs, "monitors");
+
+ if (arg && (arg->Flags & COMMAND_LINE_ARGUMENT_PRESENT))
+ {
+ UINT32 numMonitors = 0;
+ MONITOR_DEF monitors[16] = { 0 };
+ numMonitors = shadow_enum_monitors(monitors, 16);
+
+ if (arg->Flags & COMMAND_LINE_VALUE_PRESENT)
+ {
+ /* Select monitors */
+ long val = strtol(arg->Value, NULL, 0);
+
+ if ((val < 0) || (errno != 0) || ((UINT32)val >= numMonitors))
+ status = COMMAND_LINE_STATUS_PRINT;
+
+ server->selectedMonitor = (UINT32)val;
+ }
+ else
+ {
+ /* List monitors */
+
+ for (UINT32 index = 0; index < numMonitors; index++)
+ {
+ const MONITOR_DEF* monitor = &monitors[index];
+ const INT64 width = monitor->right - monitor->left + 1;
+ const INT64 height = monitor->bottom - monitor->top + 1;
+ WLog_INFO(TAG, " %s [%d] %" PRId64 "x%" PRId64 "\t+%" PRId32 "+%" PRId32 "",
+ (monitor->flags == 1) ? "*" : " ", index, width, height, monitor->left,
+ monitor->top);
+ }
+
+ status = COMMAND_LINE_STATUS_PRINT;
+ }
+ }
+
+ /* If we want to disable authentication we need to ensure that NLA security
+ * is not activated. Only TLS and RDP security allow anonymous login.
+ */
+ if (!server->authentication)
+ {
+ if (!freerdp_settings_set_bool(settings, FreeRDP_NlaSecurity, FALSE))
+ return COMMAND_LINE_ERROR;
+ }
+ return status;
+}
+
+static DWORD WINAPI shadow_server_thread(LPVOID arg)
+{
+ rdpShadowServer* server = (rdpShadowServer*)arg;
+ BOOL running = TRUE;
+ DWORD status = 0;
+ freerdp_listener* listener = server->listener;
+ shadow_subsystem_start(server->subsystem);
+
+ while (running)
+ {
+ HANDLE events[MAXIMUM_WAIT_OBJECTS] = { 0 };
+ DWORD nCount = 0;
+ events[nCount++] = server->StopEvent;
+ nCount += listener->GetEventHandles(listener, &events[nCount], ARRAYSIZE(events) - nCount);
+
+ if (nCount <= 1)
+ {
+ WLog_ERR(TAG, "Failed to get FreeRDP file descriptor");
+ break;
+ }
+
+ status = WaitForMultipleObjects(nCount, events, FALSE, INFINITE);
+
+ switch (status)
+ {
+ case WAIT_FAILED:
+ case WAIT_OBJECT_0:
+ running = FALSE;
+ break;
+
+ default:
+ {
+ if (!listener->CheckFileDescriptor(listener))
+ {
+ WLog_ERR(TAG, "Failed to check FreeRDP file descriptor");
+ running = FALSE;
+ }
+ else
+ {
+#ifdef _WIN32
+ Sleep(100); /* FIXME: listener event handles */
+#endif
+ }
+ }
+ break;
+ }
+ }
+
+ listener->Close(listener);
+ shadow_subsystem_stop(server->subsystem);
+
+ /* Signal to the clients that server is being stopped and wait for them
+ * to disconnect. */
+ if (shadow_client_boardcast_quit(server, 0))
+ {
+ while (ArrayList_Count(server->clients) > 0)
+ {
+ Sleep(100);
+ }
+ }
+
+ ExitThread(0);
+ return 0;
+}
+
+static BOOL open_port(rdpShadowServer* server, char* address)
+{
+ BOOL status = 0;
+ char* modaddr = address;
+
+ if (modaddr)
+ {
+ if (modaddr[0] == '[')
+ {
+ char* end = strchr(address, ']');
+ if (!end)
+ {
+ WLog_ERR(TAG, "Could not parse bind-address %s", address);
+ return -1;
+ }
+ *end++ = '\0';
+ if (strlen(end) > 0)
+ {
+ WLog_ERR(TAG, "Excess data after IPv6 address: '%s'", end);
+ return -1;
+ }
+ modaddr++;
+ }
+ }
+ status = server->listener->Open(server->listener, modaddr, (UINT16)server->port);
+
+ if (!status)
+ {
+ WLog_ERR(TAG,
+ "Problem creating TCP listener. (Port already used or insufficient permissions?)");
+ }
+
+ return status;
+}
+
+int shadow_server_start(rdpShadowServer* server)
+{
+ BOOL ipc = 0;
+ BOOL status = 0;
+ WSADATA wsaData;
+
+ if (!server)
+ return -1;
+
+ if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
+ return -1;
+
+#ifndef _WIN32
+ signal(SIGPIPE, SIG_IGN);
+#endif
+ server->screen = shadow_screen_new(server);
+
+ if (!server->screen)
+ {
+ WLog_ERR(TAG, "screen_new failed");
+ return -1;
+ }
+
+ server->capture = shadow_capture_new(server);
+
+ if (!server->capture)
+ {
+ WLog_ERR(TAG, "capture_new failed");
+ return -1;
+ }
+
+ /* Bind magic:
+ *
+ * emtpy ... bind TCP all
+ * <local path> ... bind local (IPC)
+ * bind-socket,<address> ... bind TCP to specified interface
+ */
+ ipc = server->ipcSocket && (strncmp(bind_address, server->ipcSocket,
+ strnlen(bind_address, sizeof(bind_address))) != 0);
+ if (!ipc)
+ {
+ size_t count = 0;
+ char** list = CommandLineParseCommaSeparatedValuesEx(NULL, server->ipcSocket, &count);
+ if (!list || (count <= 1))
+ {
+ if (server->ipcSocket == NULL)
+ {
+ if (!open_port(server, NULL))
+ {
+ free(list);
+ return -1;
+ }
+ }
+ else
+ {
+ free(list);
+ return -1;
+ }
+ }
+
+ WINPR_ASSERT(list || (count == 0));
+ for (size_t x = 1; x < count; x++)
+ {
+ BOOL success = open_port(server, list[x]);
+ if (!success)
+ {
+ free(list);
+ return -1;
+ }
+ }
+ free(list);
+ }
+ else
+ {
+ status = server->listener->OpenLocal(server->listener, server->ipcSocket);
+
+ if (!status)
+ {
+ WLog_ERR(TAG, "Problem creating local socket listener. (Port already used or "
+ "insufficient permissions?)");
+ return -1;
+ }
+ }
+
+ if (!(server->thread = CreateThread(NULL, 0, shadow_server_thread, (void*)server, 0, NULL)))
+ {
+ return -1;
+ }
+
+ return 0;
+}
+
+int shadow_server_stop(rdpShadowServer* server)
+{
+ if (!server)
+ return -1;
+
+ if (server->thread)
+ {
+ SetEvent(server->StopEvent);
+ WaitForSingleObject(server->thread, INFINITE);
+ CloseHandle(server->thread);
+ server->thread = NULL;
+ if (server->listener && server->listener->Close)
+ server->listener->Close(server->listener);
+ }
+
+ if (server->screen)
+ {
+ shadow_screen_free(server->screen);
+ server->screen = NULL;
+ }
+
+ if (server->capture)
+ {
+ shadow_capture_free(server->capture);
+ server->capture = NULL;
+ }
+
+ return 0;
+}
+
+static int shadow_server_init_config_path(rdpShadowServer* server)
+{
+#ifdef _WIN32
+
+ if (!server->ConfigPath)
+ {
+ server->ConfigPath = GetEnvironmentSubPath("LOCALAPPDATA", "freerdp");
+ }
+
+#endif
+#ifdef __APPLE__
+
+ if (!server->ConfigPath)
+ {
+ char* userLibraryPath;
+ char* userApplicationSupportPath;
+ userLibraryPath = GetKnownSubPath(KNOWN_PATH_HOME, "Library");
+
+ if (userLibraryPath)
+ {
+ if (!winpr_PathFileExists(userLibraryPath) && !winpr_PathMakePath(userLibraryPath, 0))
+ {
+ WLog_ERR(TAG, "Failed to create directory '%s'", userLibraryPath);
+ free(userLibraryPath);
+ return -1;
+ }
+
+ userApplicationSupportPath = GetCombinedPath(userLibraryPath, "Application Support");
+
+ if (userApplicationSupportPath)
+ {
+ if (!winpr_PathFileExists(userApplicationSupportPath) &&
+ !winpr_PathMakePath(userApplicationSupportPath, 0))
+ {
+ WLog_ERR(TAG, "Failed to create directory '%s'", userApplicationSupportPath);
+ free(userLibraryPath);
+ free(userApplicationSupportPath);
+ return -1;
+ }
+
+ server->ConfigPath = GetCombinedPath(userApplicationSupportPath, "freerdp");
+ }
+
+ free(userLibraryPath);
+ free(userApplicationSupportPath);
+ }
+ }
+
+#endif
+
+ if (!server->ConfigPath)
+ {
+ char* configHome = NULL;
+ configHome = GetKnownPath(KNOWN_PATH_XDG_CONFIG_HOME);
+
+ if (configHome)
+ {
+ if (!winpr_PathFileExists(configHome) && !winpr_PathMakePath(configHome, 0))
+ {
+ WLog_ERR(TAG, "Failed to create directory '%s'", configHome);
+ free(configHome);
+ return -1;
+ }
+
+ server->ConfigPath = GetKnownSubPath(KNOWN_PATH_XDG_CONFIG_HOME, "freerdp");
+ free(configHome);
+ }
+ }
+
+ if (!server->ConfigPath)
+ return -1; /* no usable config path */
+
+ return 1;
+}
+
+static BOOL shadow_server_create_certificate(rdpShadowServer* server, const char* filepath)
+{
+ BOOL rc = FALSE;
+ char* makecert_argv[6] = { "makecert", "-rdp", "-live", "-silent", "-y", "5" };
+ const size_t makecert_argc = ARRAYSIZE(makecert_argv);
+
+ MAKECERT_CONTEXT* makecert = makecert_context_new();
+
+ if (!makecert)
+ goto out_fail;
+
+ if (makecert_context_process(makecert, makecert_argc, makecert_argv) < 0)
+ goto out_fail;
+
+ if (makecert_context_set_output_file_name(makecert, "shadow") != 1)
+ goto out_fail;
+
+ WINPR_ASSERT(server);
+ WINPR_ASSERT(filepath);
+ if (!winpr_PathFileExists(server->CertificateFile))
+ {
+ if (makecert_context_output_certificate_file(makecert, filepath) != 1)
+ goto out_fail;
+ }
+
+ if (!winpr_PathFileExists(server->PrivateKeyFile))
+ {
+ if (makecert_context_output_private_key_file(makecert, filepath) != 1)
+ goto out_fail;
+ }
+ rc = TRUE;
+out_fail:
+ makecert_context_free(makecert);
+ return rc;
+}
+static BOOL shadow_server_init_certificate(rdpShadowServer* server)
+{
+ char* filepath = NULL;
+ BOOL ret = FALSE;
+
+ WINPR_ASSERT(server);
+
+ if (!winpr_PathFileExists(server->ConfigPath) && !winpr_PathMakePath(server->ConfigPath, 0))
+ {
+ WLog_ERR(TAG, "Failed to create directory '%s'", server->ConfigPath);
+ return FALSE;
+ }
+
+ if (!(filepath = GetCombinedPath(server->ConfigPath, "shadow")))
+ return FALSE;
+
+ if (!winpr_PathFileExists(filepath) && !winpr_PathMakePath(filepath, 0))
+ {
+ if (!CreateDirectoryA(filepath, 0))
+ {
+ WLog_ERR(TAG, "Failed to create directory '%s'", filepath);
+ goto out_fail;
+ }
+ }
+
+ server->CertificateFile = GetCombinedPath(filepath, "shadow.crt");
+ server->PrivateKeyFile = GetCombinedPath(filepath, "shadow.key");
+
+ if (!server->CertificateFile || !server->PrivateKeyFile)
+ goto out_fail;
+
+ if ((!winpr_PathFileExists(server->CertificateFile)) ||
+ (!winpr_PathFileExists(server->PrivateKeyFile)))
+ {
+ if (!shadow_server_create_certificate(server, filepath))
+ goto out_fail;
+ }
+
+ rdpSettings* settings = server->settings;
+ WINPR_ASSERT(settings);
+
+ rdpPrivateKey* key = freerdp_key_new_from_file(server->PrivateKeyFile);
+ if (!key)
+ goto out_fail;
+ if (!freerdp_settings_set_pointer_len(settings, FreeRDP_RdpServerRsaKey, key, 1))
+ goto out_fail;
+
+ rdpCertificate* cert = freerdp_certificate_new_from_file(server->CertificateFile);
+ if (!cert)
+ goto out_fail;
+
+ if (!freerdp_settings_set_pointer_len(settings, FreeRDP_RdpServerCertificate, cert, 1))
+ goto out_fail;
+
+ if (!freerdp_certificate_is_rdp_security_compatible(cert))
+ {
+ if (!freerdp_settings_set_bool(settings, FreeRDP_UseRdpSecurityLayer, FALSE))
+ goto out_fail;
+ if (!freerdp_settings_set_bool(settings, FreeRDP_RdpSecurity, FALSE))
+ goto out_fail;
+ }
+ ret = TRUE;
+out_fail:
+ free(filepath);
+ return ret;
+}
+
+static BOOL shadow_server_check_peer_restrictions(freerdp_listener* listener)
+{
+ WINPR_ASSERT(listener);
+
+ rdpShadowServer* server = (rdpShadowServer*)listener->info;
+ WINPR_ASSERT(server);
+
+ if (server->maxClientsConnected > 0)
+ {
+ const size_t count = ArrayList_Count(server->clients);
+ if (count >= server->maxClientsConnected)
+ {
+ WLog_WARN(TAG, "connection limit [%" PRIuz "] reached, discarding client",
+ server->maxClientsConnected);
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+int shadow_server_init(rdpShadowServer* server)
+{
+ int status = 0;
+ winpr_InitializeSSL(WINPR_SSL_INIT_DEFAULT);
+ WTSRegisterWtsApiFunctionTable(FreeRDP_InitWtsApi());
+
+ if (!(server->clients = ArrayList_New(TRUE)))
+ goto fail;
+
+ if (!(server->StopEvent = CreateEvent(NULL, TRUE, FALSE, NULL)))
+ goto fail;
+
+ if (!InitializeCriticalSectionAndSpinCount(&(server->lock), 4000))
+ goto fail;
+
+ status = shadow_server_init_config_path(server);
+
+ if (status < 0)
+ goto fail;
+
+ if (!shadow_server_init_certificate(server))
+ goto fail;
+
+ server->listener = freerdp_listener_new();
+
+ if (!server->listener)
+ goto fail;
+
+ server->listener->info = (void*)server;
+ server->listener->CheckPeerAcceptRestrictions = shadow_server_check_peer_restrictions;
+ server->listener->PeerAccepted = shadow_client_accepted;
+ server->subsystem = shadow_subsystem_new();
+
+ if (!server->subsystem)
+ goto fail;
+
+ status = shadow_subsystem_init(server->subsystem, server);
+ if (status < 0)
+ goto fail;
+
+ return status;
+
+fail:
+ shadow_server_uninit(server);
+ WLog_ERR(TAG, "Failed to initialize shadow server");
+ return -1;
+}
+
+int shadow_server_uninit(rdpShadowServer* server)
+{
+ if (!server)
+ return -1;
+
+ shadow_server_stop(server);
+ shadow_subsystem_uninit(server->subsystem);
+ shadow_subsystem_free(server->subsystem);
+ server->subsystem = NULL;
+ freerdp_listener_free(server->listener);
+ server->listener = NULL;
+ free(server->CertificateFile);
+ server->CertificateFile = NULL;
+ free(server->PrivateKeyFile);
+ server->PrivateKeyFile = NULL;
+ free(server->ConfigPath);
+ server->ConfigPath = NULL;
+ DeleteCriticalSection(&(server->lock));
+ CloseHandle(server->StopEvent);
+ server->StopEvent = NULL;
+ ArrayList_Free(server->clients);
+ server->clients = NULL;
+ return 1;
+}
+
+rdpShadowServer* shadow_server_new(void)
+{
+ rdpShadowServer* server = NULL;
+ server = (rdpShadowServer*)calloc(1, sizeof(rdpShadowServer));
+
+ if (!server)
+ return NULL;
+
+ server->port = 3389;
+ server->mayView = TRUE;
+ server->mayInteract = TRUE;
+ server->rfxMode = RLGR3;
+ server->h264RateControlMode = H264_RATECONTROL_VBR;
+ server->h264BitRate = 10000000;
+ server->h264FrameRate = 30;
+ server->h264QP = 0;
+ server->authentication = TRUE;
+ server->settings = freerdp_settings_new(FREERDP_SETTINGS_SERVER_MODE);
+ return server;
+}
+
+void shadow_server_free(rdpShadowServer* server)
+{
+ if (!server)
+ return;
+
+ free(server->ipcSocket);
+ server->ipcSocket = NULL;
+ freerdp_settings_free(server->settings);
+ server->settings = NULL;
+ free(server);
+}
diff --git a/server/shadow/shadow_subsystem.c b/server/shadow/shadow_subsystem.c
new file mode 100644
index 0000000..ca73c72
--- /dev/null
+++ b/server/shadow/shadow_subsystem.c
@@ -0,0 +1,286 @@
+/**
+ * FreeRDP: A Remote Desktop Protocol Implementation
+ *
+ * Copyright 2014 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.
+ */
+
+#include <freerdp/config.h>
+
+#include "shadow.h"
+
+#include "shadow_subsystem.h"
+
+static pfnShadowSubsystemEntry pSubsystemEntry = NULL;
+
+void shadow_subsystem_set_entry(pfnShadowSubsystemEntry pEntry)
+{
+ pSubsystemEntry = pEntry;
+}
+
+static int shadow_subsystem_load_entry_points(RDP_SHADOW_ENTRY_POINTS* pEntryPoints)
+{
+ WINPR_ASSERT(pEntryPoints);
+ ZeroMemory(pEntryPoints, sizeof(RDP_SHADOW_ENTRY_POINTS));
+
+ if (!pSubsystemEntry)
+ return -1;
+
+ if (pSubsystemEntry(pEntryPoints) < 0)
+ return -1;
+
+ return 1;
+}
+
+rdpShadowSubsystem* shadow_subsystem_new(void)
+{
+ RDP_SHADOW_ENTRY_POINTS ep;
+ rdpShadowSubsystem* subsystem = NULL;
+
+ shadow_subsystem_load_entry_points(&ep);
+
+ if (!ep.New)
+ return NULL;
+
+ subsystem = ep.New();
+
+ if (!subsystem)
+ return NULL;
+
+ CopyMemory(&(subsystem->ep), &ep, sizeof(RDP_SHADOW_ENTRY_POINTS));
+
+ return subsystem;
+}
+
+void shadow_subsystem_free(rdpShadowSubsystem* subsystem)
+{
+ if (subsystem && subsystem->ep.Free)
+ subsystem->ep.Free(subsystem);
+}
+
+int shadow_subsystem_init(rdpShadowSubsystem* subsystem, rdpShadowServer* server)
+{
+ int status = -1;
+
+ if (!subsystem || !subsystem->ep.Init)
+ return -1;
+
+ subsystem->server = server;
+ subsystem->selectedMonitor = server->selectedMonitor;
+
+ if (!(subsystem->MsgPipe = MessagePipe_New()))
+ goto fail;
+
+ if (!(subsystem->updateEvent = shadow_multiclient_new()))
+ goto fail;
+
+ if ((status = subsystem->ep.Init(subsystem)) >= 0)
+ return status;
+
+fail:
+ if (subsystem->MsgPipe)
+ {
+ MessagePipe_Free(subsystem->MsgPipe);
+ subsystem->MsgPipe = NULL;
+ }
+
+ if (subsystem->updateEvent)
+ {
+ shadow_multiclient_free(subsystem->updateEvent);
+ subsystem->updateEvent = NULL;
+ }
+
+ return status;
+}
+
+static void shadow_subsystem_free_queued_message(void* obj)
+{
+ wMessage* message = (wMessage*)obj;
+ if (message->Free)
+ {
+ message->Free(message);
+ message->Free = NULL;
+ }
+}
+
+void shadow_subsystem_uninit(rdpShadowSubsystem* subsystem)
+{
+ if (!subsystem)
+ return;
+
+ if (subsystem->ep.Uninit)
+ subsystem->ep.Uninit(subsystem);
+
+ if (subsystem->MsgPipe)
+ {
+ wObject* obj1 = NULL;
+ wObject* obj2 = NULL;
+ /* Release resource in messages before free */
+ obj1 = MessageQueue_Object(subsystem->MsgPipe->In);
+
+ obj1->fnObjectFree = shadow_subsystem_free_queued_message;
+ MessageQueue_Clear(subsystem->MsgPipe->In);
+
+ obj2 = MessageQueue_Object(subsystem->MsgPipe->Out);
+ obj2->fnObjectFree = shadow_subsystem_free_queued_message;
+ MessageQueue_Clear(subsystem->MsgPipe->Out);
+ MessagePipe_Free(subsystem->MsgPipe);
+ subsystem->MsgPipe = NULL;
+ }
+
+ if (subsystem->updateEvent)
+ {
+ shadow_multiclient_free(subsystem->updateEvent);
+ subsystem->updateEvent = NULL;
+ }
+}
+
+int shadow_subsystem_start(rdpShadowSubsystem* subsystem)
+{
+ int status = 0;
+
+ if (!subsystem || !subsystem->ep.Start)
+ return -1;
+
+ status = subsystem->ep.Start(subsystem);
+
+ return status;
+}
+
+int shadow_subsystem_stop(rdpShadowSubsystem* subsystem)
+{
+ int status = 0;
+
+ if (!subsystem || !subsystem->ep.Stop)
+ return -1;
+
+ status = subsystem->ep.Stop(subsystem);
+
+ return status;
+}
+
+UINT32 shadow_enum_monitors(MONITOR_DEF* monitors, UINT32 maxMonitors)
+{
+ UINT32 numMonitors = 0;
+ RDP_SHADOW_ENTRY_POINTS ep;
+
+ if (shadow_subsystem_load_entry_points(&ep) < 0)
+ return 0;
+
+ numMonitors = ep.EnumMonitors(monitors, maxMonitors);
+
+ return numMonitors;
+}
+
+/**
+ * Common function for subsystem implementation.
+ * This function convert 32bit ARGB format pixels to xormask data
+ * and andmask data and fill into SHADOW_MSG_OUT_POINTER_ALPHA_UPDATE
+ * Caller should free the andMaskData and xorMaskData later.
+ */
+int shadow_subsystem_pointer_convert_alpha_pointer_data(
+ BYTE* pixels, BOOL premultiplied, UINT32 width, UINT32 height,
+ SHADOW_MSG_OUT_POINTER_ALPHA_UPDATE* pointerColor)
+{
+ BYTE* pSrc8 = NULL;
+ BYTE* pDst8 = NULL;
+ UINT32 xorStep = 0;
+ UINT32 andStep = 0;
+ UINT32 andBit = 0;
+ BYTE* andBits = NULL;
+ UINT32 andPixel = 0;
+ BYTE A = 0;
+ BYTE R = 0;
+ BYTE G = 0;
+ BYTE B = 0;
+
+ xorStep = (width * 3);
+ xorStep += (xorStep % 2);
+
+ andStep = ((width + 7) / 8);
+ andStep += (andStep % 2);
+
+ pointerColor->lengthXorMask = height * xorStep;
+ pointerColor->xorMaskData = (BYTE*)calloc(1, pointerColor->lengthXorMask);
+
+ if (!pointerColor->xorMaskData)
+ return -1;
+
+ pointerColor->lengthAndMask = height * andStep;
+ pointerColor->andMaskData = (BYTE*)calloc(1, pointerColor->lengthAndMask);
+
+ if (!pointerColor->andMaskData)
+ {
+ free(pointerColor->xorMaskData);
+ pointerColor->xorMaskData = NULL;
+ return -1;
+ }
+
+ for (UINT32 y = 0; y < height; y++)
+ {
+ pSrc8 = &pixels[(width * 4) * (height - 1 - y)];
+ pDst8 = &(pointerColor->xorMaskData[y * xorStep]);
+
+ andBit = 0x80;
+ andBits = &(pointerColor->andMaskData[andStep * y]);
+
+ for (UINT32 x = 0; x < width; x++)
+ {
+ B = *pSrc8++;
+ G = *pSrc8++;
+ R = *pSrc8++;
+ A = *pSrc8++;
+
+ andPixel = 0;
+
+ if (A < 64)
+ A = 0; /* pixel cannot be partially transparent */
+
+ if (!A)
+ {
+ /* transparent pixel: XOR = black, AND = 1 */
+ andPixel = 1;
+ B = G = R = 0;
+ }
+ else
+ {
+ if (premultiplied)
+ {
+ B = (B * 0xFF) / A;
+ G = (G * 0xFF) / A;
+ R = (R * 0xFF) / A;
+ }
+ }
+
+ *pDst8++ = B;
+ *pDst8++ = G;
+ *pDst8++ = R;
+
+ if (andPixel)
+ *andBits |= andBit;
+ if (!(andBit >>= 1))
+ {
+ andBits++;
+ andBit = 0x80;
+ }
+ }
+ }
+
+ return 1;
+}
+
+void shadow_subsystem_frame_update(rdpShadowSubsystem* subsystem)
+{
+ shadow_multiclient_publish_and_wait(subsystem->updateEvent);
+}
diff --git a/server/shadow/shadow_subsystem.h b/server/shadow/shadow_subsystem.h
new file mode 100644
index 0000000..206dfdd
--- /dev/null
+++ b/server/shadow/shadow_subsystem.h
@@ -0,0 +1,47 @@
+/**
+ * FreeRDP: A Remote Desktop Protocol Implementation
+ *
+ * Copyright 2014 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.
+ */
+
+#ifndef FREERDP_SERVER_SHADOW_SUBSYSTEM_H
+#define FREERDP_SERVER_SHADOW_SUBSYSTEM_H
+
+#include <freerdp/server/shadow.h>
+
+#include <winpr/crt.h>
+#include <winpr/synch.h>
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+ void shadow_subsystem_free(rdpShadowSubsystem* subsystem);
+
+ WINPR_ATTR_MALLOC(shadow_subsystem_free, 1)
+ rdpShadowSubsystem* shadow_subsystem_new(void);
+
+ int shadow_subsystem_init(rdpShadowSubsystem* subsystem, rdpShadowServer* server);
+ void shadow_subsystem_uninit(rdpShadowSubsystem* subsystem);
+
+ int shadow_subsystem_start(rdpShadowSubsystem* subsystem);
+ int shadow_subsystem_stop(rdpShadowSubsystem* subsystem);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* FREERDP_SERVER_SHADOW_SUBSYSTEM_H */
diff --git a/server/shadow/shadow_subsystem_builtin.c b/server/shadow/shadow_subsystem_builtin.c
new file mode 100644
index 0000000..627458e
--- /dev/null
+++ b/server/shadow/shadow_subsystem_builtin.c
@@ -0,0 +1,75 @@
+/**
+ * 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.
+ */
+
+#include <freerdp/config.h>
+
+#include <freerdp/server/shadow.h>
+
+typedef struct
+{
+ const char* (*name)(void);
+ pfnShadowSubsystemEntry entry;
+} RDP_SHADOW_SUBSYSTEM;
+
+extern int ShadowSubsystemEntry(RDP_SHADOW_ENTRY_POINTS* pEntryPoints);
+extern const char* ShadowSubsystemName(void);
+
+static RDP_SHADOW_SUBSYSTEM g_Subsystems[] = {
+
+ { ShadowSubsystemName, ShadowSubsystemEntry }
+};
+
+static size_t g_SubsystemCount = ARRAYSIZE(g_Subsystems);
+
+static pfnShadowSubsystemEntry shadow_subsystem_load_static_entry(const char* name)
+{
+ if (!name)
+ {
+ if (g_SubsystemCount > 0)
+ {
+ const RDP_SHADOW_SUBSYSTEM* cur = &g_Subsystems[0];
+ WINPR_ASSERT(cur->entry);
+
+ return cur->entry;
+ }
+
+ return NULL;
+ }
+
+ for (size_t index = 0; index < g_SubsystemCount; index++)
+ {
+ const RDP_SHADOW_SUBSYSTEM* cur = &g_Subsystems[index];
+ WINPR_ASSERT(cur->name);
+ WINPR_ASSERT(cur->entry);
+
+ if (strcmp(name, cur->name()) == 0)
+ return cur->entry;
+ }
+
+ return NULL;
+}
+
+void shadow_subsystem_set_entry_builtin(const char* name)
+{
+ pfnShadowSubsystemEntry entry = shadow_subsystem_load_static_entry(name);
+
+ if (entry)
+ shadow_subsystem_set_entry(entry);
+
+ return;
+}
diff --git a/server/shadow/shadow_surface.c b/server/shadow/shadow_surface.c
new file mode 100644
index 0000000..16aecce
--- /dev/null
+++ b/server/shadow/shadow_surface.c
@@ -0,0 +1,104 @@
+/**
+ * FreeRDP: A Remote Desktop Protocol Implementation
+ *
+ * Copyright 2014 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.
+ */
+
+#include <freerdp/config.h>
+
+#include "shadow.h"
+
+#include "shadow_surface.h"
+#define ALIGN_SCREEN_SIZE(size, align) \
+ ((((size) % (align)) != 0) ? ((size) + (align) - ((size) % (align))) : (size))
+
+rdpShadowSurface* shadow_surface_new(rdpShadowServer* server, UINT16 x, UINT16 y, UINT32 width,
+ UINT32 height)
+{
+ rdpShadowSurface* surface = NULL;
+ surface = (rdpShadowSurface*)calloc(1, sizeof(rdpShadowSurface));
+
+ if (!surface)
+ return NULL;
+
+ surface->server = server;
+ surface->x = x;
+ surface->y = y;
+ surface->width = width;
+ surface->height = height;
+ surface->scanline = ALIGN_SCREEN_SIZE(surface->width, 32) * 4;
+ surface->format = PIXEL_FORMAT_BGRX32;
+ surface->data = (BYTE*)calloc(ALIGN_SCREEN_SIZE(surface->height, 32), surface->scanline);
+
+ if (!surface->data)
+ {
+ free(surface);
+ return NULL;
+ }
+
+ if (!InitializeCriticalSectionAndSpinCount(&(surface->lock), 4000))
+ {
+ free(surface->data);
+ free(surface);
+ return NULL;
+ }
+
+ region16_init(&(surface->invalidRegion));
+ return surface;
+}
+
+void shadow_surface_free(rdpShadowSurface* surface)
+{
+ if (!surface)
+ return;
+
+ free(surface->data);
+ DeleteCriticalSection(&(surface->lock));
+ region16_uninit(&(surface->invalidRegion));
+ free(surface);
+}
+
+BOOL shadow_surface_resize(rdpShadowSurface* surface, UINT16 x, UINT16 y, UINT32 width,
+ UINT32 height)
+{
+ BYTE* buffer = NULL;
+ UINT32 scanline = ALIGN_SCREEN_SIZE(width, 4) * 4;
+
+ if (!surface)
+ return FALSE;
+
+ if ((width == surface->width) && (height == surface->height))
+ {
+ /* We don't need to reset frame buffer, just update left top */
+ surface->x = x;
+ surface->y = y;
+ return TRUE;
+ }
+
+ buffer = (BYTE*)realloc(surface->data, 1ull * scanline * ALIGN_SCREEN_SIZE(height, 4ull));
+
+ if (buffer)
+ {
+ surface->x = x;
+ surface->y = y;
+ surface->width = width;
+ surface->height = height;
+ surface->scanline = scanline;
+ surface->data = buffer;
+ return TRUE;
+ }
+
+ return FALSE;
+}
diff --git a/server/shadow/shadow_surface.h b/server/shadow/shadow_surface.h
new file mode 100644
index 0000000..277df0a
--- /dev/null
+++ b/server/shadow/shadow_surface.h
@@ -0,0 +1,45 @@
+/**
+ * FreeRDP: A Remote Desktop Protocol Implementation
+ *
+ * Copyright 2014 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.
+ */
+
+#ifndef FREERDP_SERVER_SHADOW_SURFACE_H
+#define FREERDP_SERVER_SHADOW_SURFACE_H
+
+#include <freerdp/server/shadow.h>
+
+#include <winpr/crt.h>
+#include <winpr/synch.h>
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+ void shadow_surface_free(rdpShadowSurface* surface);
+
+ WINPR_ATTR_MALLOC(shadow_surface_free, 1)
+ rdpShadowSurface* shadow_surface_new(rdpShadowServer* server, UINT16 x, UINT16 y, UINT32 width,
+ UINT32 height);
+
+ BOOL shadow_surface_resize(rdpShadowSurface* surface, UINT16 x, UINT16 y, UINT32 width,
+ UINT32 height);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* FREERDP_SERVER_SHADOW_SURFACE_H */