Milestone 5: deliver embedded RDP sessions and lifecycle hardening

This commit is contained in:
Keith Smith
2026-03-03 18:59:26 -07:00
parent 230a401386
commit 36006bd4aa
2941 changed files with 724359 additions and 77 deletions

View File

@@ -0,0 +1,196 @@
# 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(WITH_RDTK)
if(NOT FREERDP_UNIFIED_BUILD)
find_package(rdtk 0 REQUIRED)
include_directories(SYSTEM ${RDTK_INCLUDE_DIR})
else()
include_directories(${PROJECT_SOURCE_DIR}/rdtk/include)
include_directories(${PROJECT_BINARY_DIR}/rdtk/include)
endif()
endif()
addtargetwithresourcefile(${MODULE_NAME} "FALSE" "${FREERDP_VERSION}" SRCS)
if(WITH_RDTK)
target_compile_definitions(${MODULE_NAME} PRIVATE WITH_RDTK)
list(APPEND LIBS rdtk)
endif()
list(APPEND LIBS freerdp freerdp-server winpr winpr-tools)
target_include_directories(${MODULE_NAME} INTERFACE $<INSTALL_INTERFACE:include>)
target_link_libraries(${MODULE_NAME} PRIVATE ${LIBS})
installwithrpath(
TARGETS
${MODULE_NAME}
COMPONENT
server
EXPORT
FreeRDP-ShadowTargets
ARCHIVE
DESTINATION
${CMAKE_INSTALL_LIBDIR}
LIBRARY
DESTINATION
${CMAKE_INSTALL_LIBDIR}
RUNTIME
DESTINATION
${CMAKE_INSTALL_BINDIR}
)
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Server/shadow")
# subsystem library
set(MODULE_NAME "freerdp-shadow-subsystem")
set(SRCS shadow_subsystem_builtin.c)
option(WITH_SHADOW_SUBSYSTEM "Build actual shadow platform subsystem implementation" ON)
if(WITH_SHADOW_SUBSYSTEM)
if(WIN32)
add_subdirectory(Win)
elseif(NOT APPLE)
add_subdirectory(X11)
elseif(APPLE AND NOT IOS)
add_subdirectory(Mac)
endif()
else()
add_subdirectory(Sample)
endif()
addtargetwithresourcefile(${MODULE_NAME} FALSE "${FREERDP_VERSION}" 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})
if(NOT BUILD_SHARED_LIBS)
installwithrpath(
TARGETS freerdp-shadow-subsystem-impl DESTINATION ${CMAKE_INSTALL_LIBDIR} EXPORT FreeRDP-ShadowTargets
)
endif()
installwithrpath(
TARGETS
${MODULE_NAME}
COMPONENT
server
EXPORT
FreeRDP-ShadowTargets
ARCHIVE
DESTINATION
${CMAKE_INSTALL_LIBDIR}
LIBRARY
DESTINATION
${CMAKE_INSTALL_LIBDIR}
RUNTIME
DESTINATION
${CMAKE_INSTALL_BINDIR}
)
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Server/shadow")
# command-line executable
option(WITH_SERVER_SHADOW_CLI "Build shadow server cli tool" ON)
if(WITH_SERVER_SHADOW_CLI)
add_subdirectory(cli)
endif()
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Server/shadow")
# Do not set Requires.Private if not a static build
if(NOT BUILD_SHARED_LIBS)
set(FREERDP_SHADOW_PC_REQUIRES_PRIVATE "freerdp${FREERDP_API_VERSION}")
set(FREERDP_SHADOW_PC_LIBRARY_PRIVATE "-ldl -lpthread")
endif()
set(FREERDP_SHADOW_PC_REQUIRES freerdp-server${FREERDP_API_VERSION})
include(pkg-config-install-prefix)
cleaning_configure_file(
${CMAKE_CURRENT_SOURCE_DIR}/freerdp-shadow.pc.in
${CMAKE_CURRENT_BINARY_DIR}/freerdp-shadow${FREERDP_VERSION_MAJOR}.pc @ONLY
)
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})

View File

@@ -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")

View File

@@ -0,0 +1,21 @@
include(WarnUnmaintained)
warn_unmaintained("mac shadow server subsystem" "-DWITH_SHADOW_SUBSYSTEM=OFF")
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_compile_definitions(WITH_PAM)
include_directories(SYSTEM ${PAM_INCLUDE_DIR})
list(APPEND LIBS ${PAM_LIBRARY})
else()
message("building without PAM authentication support")
endif()
add_compile_definitions(WITH_SHADOW_MAC)
add_library(freerdp-shadow-subsystem-impl STATIC mac_shadow.h mac_shadow.c)
target_link_libraries(freerdp-shadow-subsystem-impl PRIVATE ${LIBS})

View File

@@ -0,0 +1,681 @@
/**
* 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 = nullptr;
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, nullptr);
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_no_overlap(surface->data, surface->format, surface->scanline, x, y,
width, height, pSrcData, PIXEL_FORMAT_BGRX32, nSrcStep, x,
y, nullptr, FREERDP_FLIP_NONE);
}
LeaveCriticalSection(&(surface->lock));
IOSurfaceUnlock(frameSurface, kIOSurfaceLockReadOnly, nullptr);
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", nullptr);
keys[0] = (void*)kCGDisplayStreamShowCursor;
values[0] = (void*)kCFBooleanFalse;
opts = CFDictionaryCreate(kCFAllocatorDefault, (const void**)keys, (const void**)values, 1,
nullptr, nullptr);
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 = nullptr;
}
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(nullptr, 0, mac_shadow_subsystem_thread, (void*)subsystem, 0, nullptr)))
{
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 nullptr;
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;
}

View File

@@ -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 */

View File

@@ -0,0 +1 @@
add_library(freerdp-shadow-subsystem-impl STATIC sample_shadow.c sample_shadow.h)

View File

@@ -0,0 +1,189 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
*
* Copyright 2025 Armin Novak <anovak@thincast.com>
* Copyright 2025 Thincast Technologies GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <winpr/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 "sample_shadow.h"
#define TAG SERVER_TAG("shadow.sample")
WINPR_ATTR_NODISCARD
static BOOL sample_shadow_input_synchronize_event(WINPR_ATTR_UNUSED rdpShadowSubsystem* subsystem,
WINPR_ATTR_UNUSED rdpShadowClient* client,
WINPR_ATTR_UNUSED UINT32 flags)
{
WLog_WARN(TAG, "TODO: Implement!");
return TRUE;
}
WINPR_ATTR_NODISCARD
static BOOL sample_shadow_input_keyboard_event(WINPR_ATTR_UNUSED rdpShadowSubsystem* subsystem,
WINPR_ATTR_UNUSED rdpShadowClient* client,
WINPR_ATTR_UNUSED UINT16 flags,
WINPR_ATTR_UNUSED UINT8 code)
{
WLog_WARN(TAG, "TODO: Implement!");
return TRUE;
}
WINPR_ATTR_NODISCARD
static BOOL sample_shadow_input_unicode_keyboard_event(
WINPR_ATTR_UNUSED rdpShadowSubsystem* subsystem, WINPR_ATTR_UNUSED rdpShadowClient* client,
WINPR_ATTR_UNUSED UINT16 flags, WINPR_ATTR_UNUSED UINT16 code)
{
WLog_WARN(TAG, "TODO: Implement!");
return TRUE;
}
WINPR_ATTR_NODISCARD
static BOOL sample_shadow_input_mouse_event(WINPR_ATTR_UNUSED rdpShadowSubsystem* subsystem,
WINPR_ATTR_UNUSED rdpShadowClient* client,
WINPR_ATTR_UNUSED UINT16 flags,
WINPR_ATTR_UNUSED UINT16 x, WINPR_ATTR_UNUSED UINT16 y)
{
WLog_WARN(TAG, "TODO: Implement!");
return TRUE;
}
WINPR_ATTR_NODISCARD
static BOOL sample_shadow_input_extended_mouse_event(
WINPR_ATTR_UNUSED rdpShadowSubsystem* subsystem, WINPR_ATTR_UNUSED rdpShadowClient* client,
WINPR_ATTR_UNUSED UINT16 flags, WINPR_ATTR_UNUSED UINT16 x, WINPR_ATTR_UNUSED UINT16 y)
{
WLog_WARN(TAG, "TODO: Implement!");
return TRUE;
}
WINPR_ATTR_NODISCARD
static UINT32 sample_shadow_enum_monitors(WINPR_ATTR_UNUSED MONITOR_DEF* monitors,
WINPR_ATTR_UNUSED UINT32 maxMonitors)
{
WLog_WARN(TAG, "TODO: Implement!");
return 0;
}
WINPR_ATTR_NODISCARD
static int sample_shadow_subsystem_init(rdpShadowSubsystem* arg)
{
sampleShadowSubsystem* subsystem = (sampleShadowSubsystem*)arg;
WINPR_ASSERT(subsystem);
subsystem->base.numMonitors = sample_shadow_enum_monitors(subsystem->base.monitors, 16);
WLog_WARN(TAG, "TODO: Implement!");
MONITOR_DEF* virtualScreen = &(subsystem->base.virtualScreen);
virtualScreen->left = 0;
virtualScreen->top = 0;
virtualScreen->right = 0;
virtualScreen->bottom = 0;
virtualScreen->flags = 1;
return 1;
}
WINPR_ATTR_NODISCARD
static int sample_shadow_subsystem_uninit(rdpShadowSubsystem* arg)
{
sampleShadowSubsystem* subsystem = (sampleShadowSubsystem*)arg;
if (!subsystem)
return -1;
WLog_WARN(TAG, "TODO: Implement!");
return 1;
}
WINPR_ATTR_NODISCARD
static int sample_shadow_subsystem_start(rdpShadowSubsystem* arg)
{
sampleShadowSubsystem* subsystem = (sampleShadowSubsystem*)arg;
if (!subsystem)
return -1;
WLog_WARN(TAG, "TODO: Implement!");
return 1;
}
WINPR_ATTR_NODISCARD
static int sample_shadow_subsystem_stop(rdpShadowSubsystem* arg)
{
sampleShadowSubsystem* subsystem = (sampleShadowSubsystem*)arg;
if (!subsystem)
return -1;
WLog_WARN(TAG, "TODO: Implement!");
return 1;
}
static void sample_shadow_subsystem_free(rdpShadowSubsystem* arg)
{
sampleShadowSubsystem* subsystem = (sampleShadowSubsystem*)arg;
if (!subsystem)
return;
sample_shadow_subsystem_uninit(arg);
free(subsystem);
}
WINPR_ATTR_MALLOC(sample_shadow_subsystem_free, 1)
WINPR_ATTR_NODISCARD
static rdpShadowSubsystem* sample_shadow_subsystem_new(void)
{
sampleShadowSubsystem* subsystem =
(sampleShadowSubsystem*)calloc(1, sizeof(sampleShadowSubsystem));
if (!subsystem)
return nullptr;
subsystem->base.SynchronizeEvent = sample_shadow_input_synchronize_event;
subsystem->base.KeyboardEvent = sample_shadow_input_keyboard_event;
subsystem->base.UnicodeKeyboardEvent = sample_shadow_input_unicode_keyboard_event;
subsystem->base.MouseEvent = sample_shadow_input_mouse_event;
subsystem->base.ExtendedMouseEvent = sample_shadow_input_extended_mouse_event;
return &subsystem->base;
}
const char* ShadowSubsystemName(void)
{
return "Sample";
}
int ShadowSubsystemEntry(RDP_SHADOW_ENTRY_POINTS* pEntryPoints)
{
pEntryPoints->New = sample_shadow_subsystem_new;
pEntryPoints->Free = sample_shadow_subsystem_free;
pEntryPoints->Init = sample_shadow_subsystem_init;
pEntryPoints->Uninit = sample_shadow_subsystem_uninit;
pEntryPoints->Start = sample_shadow_subsystem_start;
pEntryPoints->Stop = sample_shadow_subsystem_stop;
pEntryPoints->EnumMonitors = sample_shadow_enum_monitors;
return 1;
}

View File

@@ -0,0 +1,44 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
*
* Copyright 2025 Armin Novak <anovak@thincast.com>
* Copyright 2025 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.
*/
#pragma once
#include <freerdp/server/shadow.h>
#ifdef __cplusplus
extern "C"
{
#endif
typedef struct sample_shadow_subsystem sampleShadowSubsystem;
struct sample_shadow_subsystem
{
rdpShadowSubsystem base;
/* Additional platform specific stuff goes here */
};
FREERDP_API WINPR_ATTR_NODISCARD const char* ShadowSubsystemName(void);
FREERDP_API WINPR_ATTR_NODISCARD int
ShadowSubsystemEntry(RDP_SHADOW_ENTRY_POINTS* pEntryPoints);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,16 @@
include(WarnUnmaintained)
warn_unmaintained("windows shadow server subsystem" "-DWITH_SHADOW_SUBSYSTEM=OFF")
add_compile_definitions(WITH_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)

View File

@@ -0,0 +1,795 @@
/**
* 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 <winpr/library.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 = nullptr;
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 = nullptr;
#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 = GetProcAddressAs(d3d11_module, "D3D11CreateDevice", fnD3D11CreateDevice);
}
int win_shadow_dxgi_init_duplication(winShadowSubsystem* subsystem)
{
HRESULT hr;
UINT dTop, i = 0;
IDXGIOutput* pOutput;
DXGI_OUTPUT_DESC outputDesc = WINPR_C_ARRAY_INIT;
DXGI_OUTPUT_DESC* pOutputDesc;
D3D11_TEXTURE2D_DESC textureDesc;
IDXGIDevice* dxgiDevice = nullptr;
IDXGIAdapter* dxgiAdapter = nullptr;
IDXGIOutput* dxgiOutput = nullptr;
IDXGIOutput1* dxgiOutput1 = nullptr;
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 = nullptr;
}
if (FAILED(hr))
{
WLog_ERR(TAG, "IDXGIDevice::GetParent(IDXGIAdapter) failure: %s (0x%08lX)",
GetDxgiErrorString(hr), hr);
return -1;
}
pOutput = nullptr;
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 = nullptr;
}
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 = nullptr;
}
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 = nullptr;
}
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,
nullptr, &(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 = nullptr;
IDXGIAdapter* DxgiAdapter = nullptr;
IDXGIOutput* DxgiOutput = nullptr;
IDXGIOutput1* DxgiOutput1 = nullptr;
win_shadow_d3d11_module_init();
if (!pfnD3D11CreateDevice)
return -1;
for (DriverTypeIndex = 0; DriverTypeIndex < NumDriverTypes; ++DriverTypeIndex)
{
hr = pfnD3D11CreateDevice(nullptr, DriverTypes[DriverTypeIndex], nullptr, 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 = nullptr;
}
if (subsystem->dxgiDesktopImage)
{
subsystem->dxgiDesktopImage->lpVtbl->Release(subsystem->dxgiDesktopImage);
subsystem->dxgiDesktopImage = nullptr;
}
if (subsystem->dxgiOutputDuplication)
{
subsystem->dxgiOutputDuplication->lpVtbl->Release(subsystem->dxgiOutputDuplication);
subsystem->dxgiOutputDuplication = nullptr;
}
if (subsystem->dxgiDeviceContext)
{
subsystem->dxgiDeviceContext->lpVtbl->Release(subsystem->dxgiDeviceContext);
subsystem->dxgiDeviceContext = nullptr;
}
if (subsystem->dxgiDevice)
{
subsystem->dxgiDevice->lpVtbl->Release(subsystem->dxgiDevice);
subsystem->dxgiDevice = nullptr;
}
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 = nullptr;
}
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 = nullptr;
if (subsystem->dxgiFrameAcquired)
{
win_shadow_dxgi_release_frame_data(subsystem);
}
if (subsystem->dxgiDesktopImage)
{
subsystem->dxgiDesktopImage->lpVtbl->Release(subsystem->dxgiDesktopImage);
subsystem->dxgiDesktopImage = nullptr;
}
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 = nullptr;
}
if (subsystem->dxgiOutputDuplication)
{
subsystem->dxgiOutputDuplication->lpVtbl->Release(subsystem->dxgiOutputDuplication);
subsystem->dxgiOutputDuplication = nullptr;
}
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 = nullptr;
}
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

View File

@@ -0,0 +1,62 @@
/**
* 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
WINPR_ATTR_NODISCARD int win_shadow_dxgi_init(winShadowSubsystem* subsystem);
WINPR_ATTR_NODISCARD int win_shadow_dxgi_uninit(winShadowSubsystem* subsystem);
WINPR_ATTR_NODISCARD int win_shadow_dxgi_fetch_frame_data(winShadowSubsystem* subsystem,
BYTE** ppDstData, int* pnDstStep,
int x, int y, int width, int height);
WINPR_ATTR_NODISCARD int win_shadow_dxgi_get_next_frame(winShadowSubsystem* subsystem);
WINPR_ATTR_NODISCARD int win_shadow_dxgi_get_invalid_region(winShadowSubsystem* subsystem);
#endif
#ifdef __cplusplus
}
#endif
#endif /* FREERDP_SERVER_SHADOW_WIN_DXGI_H */

View File

@@ -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);
}
(void)SetEvent(subsystem->RdpUpdateEnterEvent);
(void)WaitForSingleObject(subsystem->RdpUpdateLeaveEvent, INFINITE);
(void)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] = WINPR_C_ARRAY_INIT;
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(nullptr, 0, shw_client_thread, instance, 0, nullptr)))
{
WLog_ERR(TAG, "Failed to create thread");
return -1;
}
return 0;
}
static int shw_freerdp_client_stop(rdpContext* context)
{
shwContext* shw = (shwContext*)context;
(void)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(nullptr, TRUE, FALSE, nullptr)))
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 = nullptr;
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 = WINPR_C_ARRAY_INIT;
clientEntryPoints.Size = sizeof(RDP_CLIENT_ENTRY_POINTS);
clientEntryPoints.Version = RDP_CLIENT_INTERFACE_VERSION;
shw_RdpClientEntry(&clientEntryPoints);
if (!(subsystem->RdpUpdateEnterEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr)))
goto fail_enter_event;
if (!(subsystem->RdpUpdateLeaveEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr)))
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:
(void)CloseHandle(subsystem->RdpUpdateLeaveEvent);
fail_leave_event:
(void)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;
}

View File

@@ -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
WINPR_ATTR_NODISCARD int win_shadow_rdp_init(winShadowSubsystem* subsystem);
WINPR_ATTR_NODISCARD int win_shadow_rdp_uninit(winShadowSubsystem* subsystem);
WINPR_ATTR_NODISCARD int win_shadow_rdp_start(winShadowSubsystem* subsystem);
WINPR_ATTR_NODISCARD int win_shadow_rdp_stop(winShadowSubsystem* subsystem);
#ifdef __cplusplus
}
#endif
#endif /* FREERDP_SERVER_SHADOW_WIN_RDP_H */

View File

@@ -0,0 +1,562 @@
/**
* 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 = WINPR_C_ARRAY_INIT;
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 = WINPR_C_ARRAY_INIT;
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 = nullptr;
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_no_overlap(surface->data, surface->format, surface->scanline, x, y,
width, height, pDstData, DstFormat, nDstStep, x, y, nullptr,
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);
(void)ResetEvent(subsystem->RdpUpdateEnterEvent);
(void)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 = WINPR_C_ARRAY_INIT;
displayDevice.cb = sizeof(DISPLAY_DEVICE);
if (EnumDisplayDevices(nullptr, iDevNum, &displayDevice, 0))
{
hdc = CreateDC(displayDevice.DeviceName, nullptr, nullptr, nullptr);
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(nullptr, 0, win_shadow_subsystem_thread, (void*)subsystem, 0, nullptr)))
{
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 nullptr;
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;
}

View File

@@ -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 */

View File

@@ -0,0 +1,855 @@
/**
* 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 = nullptr;
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 = nullptr;
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 = nullptr;
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 = nullptr;
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 = WINPR_C_ARRAY_INIT;
hModule = GetModuleHandle(nullptr);
wndClassEx.cbSize = sizeof(WNDCLASSEX);
wndClassEx.style = 0;
wndClassEx.lpfnWndProc = ShadowWndProc;
wndClassEx.cbClsExtra = 0;
wndClassEx.cbWndExtra = 0;
wndClassEx.hInstance = hModule;
wndClassEx.hIcon = nullptr;
wndClassEx.hCursor = nullptr;
wndClassEx.hbrBackground = nullptr;
wndClassEx.lpszMenuName = _T("ShadowWndMenu");
wndClassEx.lpszClassName = _T("ShadowWndClass");
wndClassEx.hIconSm = nullptr;
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, nullptr);
if (!subsystem->hWnd)
{
WLog_INFO(TAG, "CreateWindowEx failure");
return -1;
}
return 1;
}
int win_shadow_wds_init(winShadowSubsystem* subsystem)
{
int status = -1;
long left = 0;
long top = 0;
long right = 0;
long bottom = 0;
BSTR bstrAuthString = nullptr;
BSTR bstrGroupName = nullptr;
BSTR bstrPassword = nullptr;
BSTR bstrPropertyName = nullptr;
VARIANT varPropertyValue;
rdpAssistanceFile* file = nullptr;
IConnectionPoint* pCP = nullptr;
IConnectionPointContainer* pCPC = nullptr;
win_shadow_wds_wnd_init(subsystem);
HRESULT hr = OleInitialize(nullptr);
if (FAILED(hr))
{
WLog_ERR(TAG, "OleInitialize() failure");
return -1;
}
hr = CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);
if (FAILED(hr))
{
WLog_ERR(TAG, "CoInitialize() failure");
return -1;
}
hr = CoCreateInstance(&CLSID_RDPSession, nullptr, CLSCTX_ALL, &IID_IRDPSRAPISharingSession,
(void**)&(subsystem->pSharingSession));
if (FAILED(hr))
{
WLog_ERR(TAG, "CoCreateInstance(IRDPSRAPISharingSession) failure: 0x%08lX", hr);
return -1;
}
IUnknown* 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;
}
DWORD 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;
}
long width = right - left;
long 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, nullptr);
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;
}
rdpSettings* settings = subsystem->shw->settings;
if (!freerdp_assistance_populate_settings_from_assistance_file(file, settings))
return -1;
if (!freerdp_settings_set_string(settings, FreeRDP_Domain, "RDP"))
return -1;
if (!freerdp_settings_set_string(settings, FreeRDP_Username, "Shadow"))
return -1;
if (!freerdp_settings_set_bool(settings, FreeRDP_AutoLogonEnabled, TRUE))
return -1;
if (!freerdp_settings_set_uint32(settings, FreeRDP_DesktopWidth, width))
return -1;
if (!freerdp_settings_set_uint32(settings, FreeRDP_DesktopHeight, height))
return -1;
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 = nullptr;
}
if (subsystem->pVirtualChannelMgr)
{
subsystem->pVirtualChannelMgr->lpVtbl->Release(subsystem->pVirtualChannelMgr);
subsystem->pVirtualChannelMgr = nullptr;
}
if (subsystem->pApplicationFilter)
{
subsystem->pApplicationFilter->lpVtbl->Release(subsystem->pApplicationFilter);
subsystem->pApplicationFilter = nullptr;
}
if (subsystem->pAttendeeMgr)
{
subsystem->pAttendeeMgr->lpVtbl->Release(subsystem->pAttendeeMgr);
subsystem->pAttendeeMgr = nullptr;
}
if (subsystem->pSessionProperties)
{
subsystem->pSessionProperties->lpVtbl->Release(subsystem->pSessionProperties);
subsystem->pSessionProperties = nullptr;
}
if (subsystem->pInvitationMgr)
{
subsystem->pInvitationMgr->lpVtbl->Release(subsystem->pInvitationMgr);
subsystem->pInvitationMgr = nullptr;
}
if (subsystem->pInvitation)
{
subsystem->pInvitation->lpVtbl->Release(subsystem->pInvitation);
subsystem->pInvitation = nullptr;
}
if (subsystem->pAssistanceFile)
{
freerdp_assistance_file_free(subsystem->pAssistanceFile);
subsystem->pAssistanceFile = nullptr;
}
if (subsystem->hWnd)
{
DestroyWindow(subsystem->hWnd);
subsystem->hWnd = nullptr;
}
if (subsystem->shw)
{
win_shadow_rdp_uninit(subsystem);
subsystem->shw = nullptr;
}
return 1;
}

View File

@@ -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
WINPR_ATTR_NODISCARD int win_shadow_wds_init(winShadowSubsystem* subsystem);
WINPR_ATTR_NODISCARD int win_shadow_wds_uninit(winShadowSubsystem* subsystem);
#ifdef __cplusplus
}
#endif
#endif /* FREERDP_SERVER_SHADOW_WIN_WDS_H */

View File

@@ -0,0 +1,67 @@
find_package(X11 REQUIRED)
if(X11_FOUND)
add_compile_definitions(WITH_X11)
include_directories(SYSTEM ${X11_INCLUDE_DIR})
list(APPEND LIBS ${X11_LIBRARIES})
endif()
if(X11_XShm_FOUND)
add_compile_definitions(WITH_XSHM)
include_directories(SYSTEM ${X11_XShm_INCLUDE_PATH})
list(APPEND LIBS ${X11_XShm_LIB})
endif()
if(X11_Xext_FOUND)
add_compile_definitions(WITH_XEXT)
list(APPEND LIBS ${X11_Xext_LIB})
endif()
if(X11_Xinerama_FOUND)
add_compile_definitions(WITH_XINERAMA)
include_directories(SYSTEM ${X11_Xinerama_INCLUDE_PATH})
list(APPEND LIBS ${X11_Xinerama_LIB})
endif()
if(X11_Xdamage_FOUND)
add_compile_definitions(WITH_XDAMAGE)
include_directories(SYSTEM ${X11_Xdamage_INCLUDE_PATH})
list(APPEND LIBS ${X11_Xdamage_LIB})
endif()
if(X11_Xfixes_FOUND)
add_compile_definitions(WITH_XFIXES)
include_directories(SYSTEM ${X11_Xfixes_INCLUDE_PATH})
list(APPEND LIBS ${X11_Xfixes_LIB})
endif()
if(X11_XTest_FOUND)
add_compile_definitions(WITH_XTEST)
include_directories(SYSTEM ${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_compile_definitions(WITH_XCURSOR)
# include_directories(SYSTEM ${X11_Xcursor_INCLUDE_PATH})
# list(APPEND LIBS ${X11_Xcursor_LIB})
#endif()
#if(X11_Xrandr_FOUND)
# add_compile_definitions(WITH_XRANDR)
# include_directories(SYSTEM ${X11_Xrandr_INCLUDE_PATH})
# list(APPEND LIBS ${X11_Xrandr_LIB})
#endif()
find_package(PAM)
if(PAM_FOUND)
add_compile_definitions(WITH_PAM)
include_directories(SYSTEM ${PAM_INCLUDE_DIR})
list(APPEND LIBS ${PAM_LIBRARY})
else()
message("building without PAM authentication support")
endif()
add_compile_definitions(WITH_SHADOW_X11)
add_library(freerdp-shadow-subsystem-impl STATIC x11_shadow.h x11_shadow.c)
target_link_libraries(freerdp-shadow-subsystem-impl PRIVATE ${LIBS})

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,115 @@
/**
* 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;
UINT64 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
UINT32 format;
};
#ifdef __cplusplus
extern "C"
{
#endif
#ifdef __cplusplus
}
#endif
#endif /* FREERDP_SERVER_SHADOW_X11_H */

View File

@@ -0,0 +1,30 @@
# FreeRDP: A Remote Desktop Protocol Implementation
# FreeRDP Shadow Server cmake build script
#
# Copyright 2025 Armin Novak <anoavk@thincast.com>
# Copyright 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.
set(MODULE_NAME "freerdp-shadow-cli")
set(SRCS shadow.c)
addtargetwithresourcefile(${MODULE_NAME} TRUE "${FREERDP_VERSION}" SRCS)
list(APPEND LIBS freerdp-shadow-subsystem freerdp-shadow freerdp winpr)
target_link_libraries(${MODULE_NAME} PRIVATE ${LIBS})
installwithrpath(TARGETS ${MODULE_NAME} DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT server)
generate_and_install_freerdp_man_from_template(${MODULE_NAME} "1" "${FREERDP_API_VERSION}")

View File

@@ -0,0 +1,93 @@
.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 USAGE
#MANPAGE_NAME@ - start the shadow server on port 3389 with NLA security, SAM database at /etc/winpr/SAM
.br
@MANPAGE_NAME@ /sam-file:SAM.db - same as above, but a custom SAM database provided as argument
.br
@MANPAGE_NAME@ -sec-nla - start the shadow server on port 3380 with TLS/NLA security. This allows authenticating against PAM with unix users. Be aware that the password is transmitted plain text like with basic HTTP auth
.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>

View File

@@ -0,0 +1,199 @@
/**
* 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>[,...]]",
nullptr, nullptr, -1, nullptr, "Set logger filters, see wLog(7) for details" },
{ "log-level", COMMAND_LINE_VALUE_REQUIRED, "[OFF|FATAL|ERROR|WARN|INFO|DEBUG|TRACE]",
nullptr, nullptr, -1, nullptr, "Set the default log level, see wLog(7) for details" },
{ "port", COMMAND_LINE_VALUE_REQUIRED, "<number>", nullptr, nullptr, -1, nullptr,
"Server port" },
{ "ipc-socket", COMMAND_LINE_VALUE_REQUIRED, "<ipc-socket>", nullptr, nullptr, -1, nullptr,
"Server IPC socket" },
{ "bind-address", COMMAND_LINE_VALUE_REQUIRED, "<bind-address>[,<another address>, ...]",
nullptr, nullptr, -1, nullptr,
"An address to bind to. Use '[<ipv6>]' for IPv6 addresses, e.g. '[::1]' for "
"localhost" },
{ "server-side-cursor", COMMAND_LINE_VALUE_BOOL, nullptr, nullptr, nullptr, -1, nullptr,
"hide mouse cursor in RDP client." },
{ "monitors", COMMAND_LINE_VALUE_OPTIONAL, "<0,1,2...>", nullptr, nullptr, -1, nullptr,
"Select or list monitors" },
{ "max-connections", COMMAND_LINE_VALUE_REQUIRED, "<number>", nullptr, nullptr, -1, nullptr,
"maximum connections allowed to server, 0 to deactivate" },
{ "mouse-relative", COMMAND_LINE_VALUE_BOOL, nullptr, nullptr, nullptr, -1, nullptr,
"enable support for relative mouse events" },
{ "rect", COMMAND_LINE_VALUE_REQUIRED, "<x,y,w,h>", nullptr, nullptr, -1, nullptr,
"Select rectangle within monitor to share" },
{ "auth", COMMAND_LINE_VALUE_BOOL, nullptr, BoolValueTrue, nullptr, -1, nullptr,
"Clients must authenticate" },
{ "remote-guard", COMMAND_LINE_VALUE_BOOL, nullptr, BoolValueFalse, nullptr, -1, nullptr,
"Remote credential guard" },
{ "restricted-admin", COMMAND_LINE_VALUE_BOOL, nullptr, BoolValueTrue, nullptr, -1, nullptr,
"Restricted Admin" },
{ "vmconnect", COMMAND_LINE_VALUE_FLAG | COMMAND_LINE_VALUE_BOOL, nullptr, BoolValueFalse,
nullptr, -1, nullptr, "Hyper-V console server (bind on vsock://1)" },
{ "may-view", COMMAND_LINE_VALUE_BOOL, nullptr, BoolValueTrue, nullptr, -1, nullptr,
"Clients may view without prompt" },
{ "may-interact", COMMAND_LINE_VALUE_BOOL, nullptr, BoolValueTrue, nullptr, -1, nullptr,
"Clients may interact without prompt" },
{ "sec", COMMAND_LINE_VALUE_REQUIRED, "<rdp|tls|nla|ext>", nullptr, nullptr, -1, nullptr,
"force specific protocol security" },
{ "sec-rdp", COMMAND_LINE_VALUE_BOOL, nullptr, BoolValueTrue, nullptr, -1, nullptr,
"rdp protocol security" },
{ "sec-tls", COMMAND_LINE_VALUE_BOOL, nullptr, BoolValueTrue, nullptr, -1, nullptr,
"tls protocol security" },
{ "sec-nla", COMMAND_LINE_VALUE_BOOL, nullptr, BoolValueTrue, nullptr, -1, nullptr,
"nla protocol security" },
{ "sec-ext", COMMAND_LINE_VALUE_BOOL, nullptr, BoolValueFalse, nullptr, -1, nullptr,
"nla extended protocol security" },
{ "sam-file", COMMAND_LINE_VALUE_REQUIRED, "<file>", nullptr, nullptr, -1, nullptr,
"NTLM SAM file for NLA authentication" },
{ "keytab", COMMAND_LINE_VALUE_REQUIRED, "<file>", nullptr, nullptr, -1, nullptr,
"Kerberos keytab file for NLA authentication" },
{ "ccache", COMMAND_LINE_VALUE_REQUIRED, "<file>", nullptr, nullptr, -1, nullptr,
"Kerberos host ccache file for NLA authentication" },
{ "tls-secrets-file", COMMAND_LINE_VALUE_REQUIRED, "<file>", nullptr, nullptr, -1, nullptr,
"file where tls secrets shall be stored" },
{ "nsc", COMMAND_LINE_VALUE_BOOL, nullptr, BoolValueTrue, nullptr, -1, nullptr,
"Allow NSC codec" },
{ "rfx", COMMAND_LINE_VALUE_BOOL, nullptr, BoolValueTrue, nullptr, -1, nullptr,
"Allow RFX surface bits" },
{ "gfx", COMMAND_LINE_VALUE_BOOL, nullptr, BoolValueTrue, nullptr, -1, nullptr,
"Allow GFX pipeline" },
{ "gfx-progressive", COMMAND_LINE_VALUE_BOOL, nullptr, BoolValueTrue, nullptr, -1, nullptr,
"Allow GFX progressive codec" },
{ "gfx-rfx", COMMAND_LINE_VALUE_BOOL, nullptr, BoolValueTrue, nullptr, -1, nullptr,
"Allow GFX RFX codec" },
{ "gfx-planar", COMMAND_LINE_VALUE_BOOL, nullptr, BoolValueTrue, nullptr, -1, nullptr,
"Allow GFX planar codec" },
{ "gfx-avc420", COMMAND_LINE_VALUE_BOOL, nullptr, BoolValueTrue, nullptr, -1, nullptr,
"Allow GFX AVC420 codec" },
{ "gfx-avc444", COMMAND_LINE_VALUE_BOOL, nullptr, BoolValueTrue, nullptr, -1, nullptr,
"Allow GFX AVC444 codec" },
{ "bitmap-compat", COMMAND_LINE_VALUE_BOOL, nullptr, BoolValueFalse, nullptr, -1, nullptr,
"Limit BitmapUpdate to 1 rectangle (fixes broken windows 11 24H2 clients)" },
{ "version", COMMAND_LINE_VALUE_FLAG | COMMAND_LINE_PRINT_VERSION, nullptr, nullptr,
nullptr, -1, nullptr, "Print version" },
{ "buildconfig", COMMAND_LINE_VALUE_FLAG | COMMAND_LINE_PRINT_BUILDCONFIG, nullptr, nullptr,
nullptr, -1, nullptr, "Print the build configuration" },
{ "help", COMMAND_LINE_VALUE_FLAG | COMMAND_LINE_PRINT_HELP, nullptr, nullptr, nullptr, -1,
"?", "Print help" },
{ nullptr, 0, nullptr, nullptr, nullptr, -1, nullptr, nullptr }
};
shadow_subsystem_set_entry_builtin(nullptr);
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_RemoteFxImageCodec, TRUE) ||
!freerdp_settings_set_uint32(settings, FreeRDP_RemoteFxRlgrMode, RLGR3) ||
!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;
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)
{
status = 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 = WINPR_C_ARRAY_INIT;
while (GetMessage(&msg, 0, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
#endif
(void)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;
}

View File

@@ -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: @FREERDP_SHADOW_PC_REQUIRES@
Requires.private: @FREERDP_SHADOW_PC_REQUIRES_PRIVATE@
Libs: -L${libdir} ${libs}
Libs.private: @FREERDP_SHADOW_PC_LIBRARY_PRIVATE@
Cflags: -I${includedir}

View File

@@ -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 */

View File

@@ -0,0 +1,106 @@
/**
* 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
#if defined(CHANNEL_AUDIN_SERVER)
WINPR_ATTR_NODISCARD
static UINT AudinServerData(audin_server_context* audin, const SNDIN_DATA* data)
{
rdpShadowClient* client = nullptr;
rdpShadowSubsystem* subsystem = nullptr;
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 (client->subsystem->nAudinFormats > SSIZE_MAX)
goto fail;
if (!audin_server_set_formats(client->audin, (SSIZE_T)client->subsystem->nAudinFormats,
client->subsystem->audinFormats))
goto fail;
}
else
{
if (!audin_server_set_formats(client->audin, -1, nullptr))
goto fail;
}
return TRUE;
fail:
audin_server_context_free(audin);
client->audin = nullptr;
#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 = nullptr;
#endif
}

View File

@@ -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
WINPR_ATTR_NODISCARD BOOL shadow_client_audin_init(rdpShadowClient* client);
void shadow_client_audin_uninit(rdpShadowClient* client);
#ifdef __cplusplus
}
#endif
#endif /* FREERDP_SERVER_SHADOW_AUDIN_H */

View File

@@ -0,0 +1,349 @@
/**
* 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"
int shadow_capture_align_clip_rect(RECTANGLE_16* rect, const 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;
}
#if !defined(WITHOUT_FREERDP_3x_DEPRECATED)
int shadow_capture_compare(const BYTE* WINPR_RESTRICT pData1, UINT32 nStep1, UINT32 nWidth,
UINT32 nHeight, const BYTE* WINPR_RESTRICT pData2, UINT32 nStep2,
RECTANGLE_16* WINPR_RESTRICT rect)
{
return shadow_capture_compare_with_format(pData1, PIXEL_FORMAT_BGRX32, nStep1, nWidth, nHeight,
pData2, PIXEL_FORMAT_BGRX32, nStep2, rect);
}
#endif
WINPR_ATTR_NODISCARD
static BOOL color_equal(UINT32 colorA, UINT32 formatA, UINT32 colorB, UINT32 formatB)
{
BYTE ar = 0;
BYTE ag = 0;
BYTE ab = 0;
BYTE aa = 0;
BYTE br = 0;
BYTE bg = 0;
BYTE bb = 0;
BYTE ba = 0;
FreeRDPSplitColor(colorA, formatA, &ar, &ag, &ab, &aa, nullptr);
FreeRDPSplitColor(colorB, formatB, &br, &bg, &bb, &ba, nullptr);
if (ar != br)
return FALSE;
if (ag != bg)
return FALSE;
if (ab != bb)
return FALSE;
if (aa != ba)
return FALSE;
return TRUE;
}
WINPR_ATTR_NODISCARD
static BOOL pixel_equal(const BYTE* WINPR_RESTRICT a, UINT32 formatA, const BYTE* WINPR_RESTRICT b,
UINT32 formatB, size_t count)
{
const size_t bppA = FreeRDPGetBytesPerPixel(formatA);
const size_t bppB = FreeRDPGetBytesPerPixel(formatB);
for (size_t x = 0; x < count; x++)
{
const UINT32 colorA = FreeRDPReadColor(&a[bppA * x], formatA);
const UINT32 colorB = FreeRDPReadColor(&b[bppB * x], formatB);
if (!color_equal(colorA, formatA, colorB, formatB))
return FALSE;
}
return TRUE;
}
WINPR_ATTR_NODISCARD
static BOOL color_equal_no_alpha(UINT32 colorA, UINT32 formatA, UINT32 colorB, UINT32 formatB)
{
BYTE ar = 0;
BYTE ag = 0;
BYTE ab = 0;
BYTE br = 0;
BYTE bg = 0;
BYTE bb = 0;
FreeRDPSplitColor(colorA, formatA, &ar, &ag, &ab, nullptr, nullptr);
FreeRDPSplitColor(colorB, formatB, &br, &bg, &bb, nullptr, nullptr);
if (ar != br)
return FALSE;
if (ag != bg)
return FALSE;
if (ab != bb)
return FALSE;
return TRUE;
}
WINPR_ATTR_NODISCARD
static BOOL pixel_equal_no_alpha(const BYTE* WINPR_RESTRICT a, UINT32 formatA,
const BYTE* WINPR_RESTRICT b, UINT32 formatB, size_t count)
{
const size_t bppA = FreeRDPGetBytesPerPixel(formatA);
const size_t bppB = FreeRDPGetBytesPerPixel(formatB);
for (size_t x = 0; x < count; x++)
{
const UINT32 colorA = FreeRDPReadColor(&a[bppA * x], formatA);
const UINT32 colorB = FreeRDPReadColor(&b[bppB * x], formatB);
if (!color_equal_no_alpha(colorA, formatA, colorB, formatB))
return FALSE;
}
return TRUE;
}
WINPR_ATTR_NODISCARD
static BOOL pixel_equal_same_format(const BYTE* WINPR_RESTRICT a, UINT32 formatA,
const BYTE* WINPR_RESTRICT b, UINT32 formatB, size_t count)
{
if (formatA != formatB)
return FALSE;
const size_t bppA = FreeRDPGetBytesPerPixel(formatA);
return memcmp(a, b, count * bppA) == 0;
}
typedef BOOL (*pixel_equal_fn_t)(const BYTE* WINPR_RESTRICT a, UINT32 formatA,
const BYTE* WINPR_RESTRICT b, UINT32 formatB, size_t count);
WINPR_ATTR_NODISCARD
static pixel_equal_fn_t get_comparison_fn(DWORD format1, DWORD format2)
{
if (format1 == format2)
return pixel_equal_same_format;
const UINT32 bpp1 = FreeRDPGetBitsPerPixel(format1);
if (!FreeRDPColorHasAlpha(format1) || !FreeRDPColorHasAlpha(format2))
{
/* In case we have RGBA32 and RGBX32 or similar assume the alpha data is equal.
* This allows us to use the fast memcmp comparison. */
if ((bpp1 == 32) && FreeRDPAreColorFormatsEqualNoAlpha(format1, format2))
{
switch (format1)
{
case PIXEL_FORMAT_ARGB32:
case PIXEL_FORMAT_XRGB32:
case PIXEL_FORMAT_ABGR32:
case PIXEL_FORMAT_XBGR32:
return pixel_equal;
case PIXEL_FORMAT_RGBA32:
case PIXEL_FORMAT_RGBX32:
case PIXEL_FORMAT_BGRA32:
case PIXEL_FORMAT_BGRX32:
return pixel_equal;
default:
break;
}
}
return pixel_equal_no_alpha;
}
else
return pixel_equal_no_alpha;
}
int shadow_capture_compare_with_format(const BYTE* WINPR_RESTRICT pData1, UINT32 format1,
UINT32 nStep1, UINT32 nWidth, UINT32 nHeight,
const BYTE* WINPR_RESTRICT pData2, UINT32 format2,
UINT32 nStep2, RECTANGLE_16* WINPR_RESTRICT rect)
{
pixel_equal_fn_t pixel_equal_fn = get_comparison_fn(format1, format2);
BOOL allEqual = TRUE;
UINT32 tw = 0;
const UINT32 nrow = (nHeight + 15) / 16;
const UINT32 ncol = (nWidth + 15) / 16;
UINT32 l = ncol + 1;
UINT32 t = nrow + 1;
UINT32 r = 0;
UINT32 b = 0;
const size_t bppA = FreeRDPGetBytesPerPixel(format1);
const size_t bppB = FreeRDPGetBytesPerPixel(format2);
const RECTANGLE_16 empty = WINPR_C_ARRAY_INIT;
WINPR_ASSERT(rect);
*rect = empty;
for (size_t ty = 0; ty < nrow; ty++)
{
BOOL rowEqual = TRUE;
size_t th = ((ty + 1) == nrow) ? (nHeight % 16) : 16;
if (!th)
th = 16;
for (size_t tx = 0; tx < ncol; tx++)
{
BOOL equal = TRUE;
tw = ((tx + 1) == ncol) ? (nWidth % 16) : 16;
if (!tw)
tw = 16;
const BYTE* p1 = &pData1[(ty * 16ULL * nStep1) + (tx * 16ull * bppA)];
const BYTE* p2 = &pData2[(ty * 16ULL * nStep2) + (tx * 16ull * bppB)];
for (size_t k = 0; k < th; k++)
{
if (!pixel_equal_fn(p1, format1, p2, format2, tw))
{
equal = FALSE;
break;
}
p1 += nStep1;
p2 += nStep2;
}
if (!equal)
{
rowEqual = FALSE;
if (l > tx)
l = (UINT32)tx;
if (r < tx)
r = (UINT32)tx;
}
}
if (!rowEqual)
{
allEqual = FALSE;
if (t > ty)
t = (UINT32)ty;
if (b < ty)
b = (UINT32)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;
return 1;
}
rdpShadowCapture* shadow_capture_new(rdpShadowServer* server)
{
WINPR_ASSERT(server);
rdpShadowCapture* capture = (rdpShadowCapture*)calloc(1, sizeof(rdpShadowCapture));
if (!capture)
return nullptr;
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 nullptr;
}
return capture;
}
void shadow_capture_free(rdpShadowCapture* capture)
{
if (!capture)
return;
DeleteCriticalSection(&(capture->lock));
free(capture);
}

View File

@@ -0,0 +1,53 @@
/**
* 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)
WINPR_ATTR_NODISCARD
rdpShadowCapture* shadow_capture_new(rdpShadowServer* server);
#ifdef __cplusplus
}
#endif
#endif /* FREERDP_SERVER_SHADOW_CAPTURE_H */

View File

@@ -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.
*/
#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))
{
if (shadow_client_encomsp_init(client) < 0)
return ERROR_NOT_READY;
}
if (WTSVirtualChannelManagerIsChannelJoined(client->vcm, REMDESK_SVC_CHANNEL_NAME))
{
if (shadow_client_remdesk_init(client) < 0)
return ERROR_NOT_READY;
}
if (WTSVirtualChannelManagerIsChannelJoined(client->vcm, RDPSND_CHANNEL_NAME))
{
if (shadow_client_rdpsnd_init(client) < 0)
return ERROR_NOT_READY;
}
if (!shadow_client_audin_init(client))
return ERROR_NOT_READY;
if (shadow_client_rdpgfx_init(client) < 0)
return ERROR_NOT_READY;
return CHANNEL_RC_OK;
}
void shadow_client_channels_free(rdpShadowClient* client)
{
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);
}

View File

@@ -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
WINPR_ATTR_NODISCARD UINT shadow_client_channels_post_connect(rdpShadowClient* client);
void shadow_client_channels_free(rdpShadowClient* client);
#ifdef __cplusplus
}
#endif
#endif /* FREERDP_SERVER_SHADOW_CHANNELS_H */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,36 @@
/**
* 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
WINPR_ATTR_NODISCARD BOOL shadow_client_accepted(freerdp_listener* listener,
freerdp_peer* peer);
#ifdef __cplusplus
}
#endif
#endif /* FREERDP_SERVER_SHADOW_CLIENT_H */

View File

@@ -0,0 +1,534 @@
/**
* 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 in-flight 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 subsystem 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;
}
WINPR_ATTR_NODISCARD
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++)
{
const size_t k = (1ULL * 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 = nullptr;
}
if (encoder->grid)
{
free((void*)encoder->grid);
encoder->grid = nullptr;
}
encoder->gridWidth = 0;
encoder->gridHeight = 0;
return 0;
}
WINPR_ATTR_NODISCARD
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;
{
const UINT32 mode =
freerdp_settings_get_uint32(encoder->server->settings, FreeRDP_RemoteFxRlgrMode);
if (!rfx_context_set_mode(encoder->rfx, WINPR_ASSERTING_INT_CAST(RLGR_MODE, mode)))
goto fail;
}
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;
}
WINPR_ATTR_NODISCARD
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) ? 1 : 0))
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;
}
WINPR_ATTR_NODISCARD
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;
}
WINPR_ATTR_NODISCARD
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;
}
WINPR_ATTR_NODISCARD
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;
}
WINPR_ATTR_NODISCARD
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;
}
WINPR_ATTR_NODISCARD
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;
if (shadow_encoder_init_grid(encoder) < 0)
return -1;
if (!encoder->bs)
encoder->bs = Stream_New(nullptr, 4ULL * encoder->maxTileWidth * encoder->maxTileHeight);
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 = nullptr;
}
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 = nullptr;
}
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 = nullptr;
}
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 = nullptr;
}
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 = nullptr;
}
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 = nullptr;
}
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 = nullptr;
}
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 = nullptr;
rdpShadowServer* server = client->server;
encoder = (rdpShadowEncoder*)calloc(1, sizeof(rdpShadowEncoder));
if (!encoder)
return nullptr;
encoder->client = client;
encoder->server = server;
encoder->fps = 16;
encoder->maxFps = 32;
if (shadow_encoder_init(encoder) < 0)
{
shadow_encoder_free(encoder);
return nullptr;
}
return encoder;
}
void shadow_encoder_free(rdpShadowEncoder* encoder)
{
if (!encoder)
return;
shadow_encoder_uninit(encoder);
free(encoder);
}

View File

@@ -0,0 +1,82 @@
/**
* 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
WINPR_ATTR_NODISCARD int shadow_encoder_reset(rdpShadowEncoder* encoder);
WINPR_ATTR_NODISCARD int shadow_encoder_prepare(rdpShadowEncoder* encoder, UINT32 codecs);
WINPR_ATTR_NODISCARD UINT32 shadow_encoder_create_frame_id(rdpShadowEncoder* encoder);
void shadow_encoder_free(rdpShadowEncoder* encoder);
WINPR_ATTR_MALLOC(shadow_encoder_free, 1)
WINPR_ATTR_NODISCARD
rdpShadowEncoder* shadow_encoder_new(rdpShadowClient* client);
#ifdef __cplusplus
}
#endif
#endif /* FREERDP_SERVER_SHADOW_ENCODER_H */

View File

@@ -0,0 +1,138 @@
/**
* 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
*/
WINPR_ATTR_NODISCARD
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) != 0;
mayInteract = (pdu->Flags & ENCOMSP_MAY_INTERACT) != 0;
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);
if (inLobby != client->inLobby)
{
if (shadow_encoder_reset(client->encoder) < 0)
return ERROR_NOT_READY;
client->inLobby = inLobby;
}
return CHANNEL_RC_OK;
}
int shadow_client_encomsp_init(rdpShadowClient* client)
{
WINPR_ASSERT(client);
EncomspServerContext* encomsp = client->encomsp = encomsp_server_context_new(client->vcm);
if (!encomsp)
return -1;
encomsp->rdpcontext = &client->context;
encomsp->custom = (void*)client;
encomsp->ChangeParticipantControlLevel = encomsp_change_participant_control_level;
if (client->encomsp)
{
const UINT rc = client->encomsp->Start(client->encomsp);
if (rc != CHANNEL_RC_OK)
return -1;
}
return 1;
}
void shadow_client_encomsp_uninit(rdpShadowClient* client)
{
WINPR_ASSERT(client);
if (client->encomsp)
{
client->encomsp->Stop(client->encomsp);
encomsp_server_context_free(client->encomsp);
client->encomsp = nullptr;
}
}

View File

@@ -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
WINPR_ATTR_NODISCARD int shadow_client_encomsp_init(rdpShadowClient* client);
void shadow_client_encomsp_uninit(rdpShadowClient* client);
#ifdef __cplusplus
}
#endif
#endif /* FREERDP_SERVER_SHADOW_ENCOMSP_H */

View File

@@ -0,0 +1,178 @@
/**
* 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 <winpr/assert.h>
#include <freerdp/config.h>
#include <freerdp/log.h>
#include "shadow.h"
#define TAG SERVER_TAG("shadow.input")
WINPR_ATTR_NODISCARD
static BOOL shadow_input_synchronize_event(rdpInput* input, UINT32 flags)
{
WINPR_ASSERT(input);
rdpShadowClient* client = (rdpShadowClient*)input->context;
WINPR_ASSERT(client);
WINPR_ASSERT(client->server);
rdpShadowSubsystem* subsystem = client->server->subsystem;
WINPR_ASSERT(subsystem);
WLog_DBG(TAG, "[%s] flags=0x%04" PRIx16, client->mayInteract ? "use" : "discard", flags);
if (!client->mayInteract)
return TRUE;
return IFCALLRESULT(TRUE, subsystem->SynchronizeEvent, subsystem, client, flags);
}
WINPR_ATTR_NODISCARD
static BOOL shadow_input_keyboard_event(rdpInput* input, UINT16 flags, UINT8 code)
{
WINPR_ASSERT(input);
rdpShadowClient* client = (rdpShadowClient*)input->context;
WINPR_ASSERT(client);
WINPR_ASSERT(client->server);
rdpShadowSubsystem* subsystem = client->server->subsystem;
WINPR_ASSERT(subsystem);
WLog_DBG(TAG, "[%s] flags=0x%04" PRIx16, client->mayInteract ? "use" : "discard", flags);
if (!client->mayInteract)
return TRUE;
return IFCALLRESULT(TRUE, subsystem->KeyboardEvent, subsystem, client, flags, code);
}
WINPR_ATTR_NODISCARD
static BOOL shadow_input_unicode_keyboard_event(rdpInput* input, UINT16 flags, UINT16 code)
{
WINPR_ASSERT(input);
rdpShadowClient* client = (rdpShadowClient*)input->context;
WINPR_ASSERT(client);
WINPR_ASSERT(client->server);
rdpShadowSubsystem* subsystem = client->server->subsystem;
WINPR_ASSERT(subsystem);
WLog_DBG(TAG, "[%s] flags=0x%04" PRIx16, client->mayInteract ? "use" : "discard", flags);
if (!client->mayInteract)
return TRUE;
return IFCALLRESULT(TRUE, subsystem->UnicodeKeyboardEvent, subsystem, client, flags, code);
}
WINPR_ATTR_NODISCARD
static BOOL shadow_input_mouse_event(rdpInput* input, UINT16 flags, UINT16 x, UINT16 y)
{
WINPR_ASSERT(input);
rdpShadowClient* client = (rdpShadowClient*)input->context;
WINPR_ASSERT(client);
WINPR_ASSERT(client->server);
rdpShadowSubsystem* subsystem = client->server->subsystem;
WINPR_ASSERT(subsystem);
if (client->server->shareSubRect)
{
x += client->server->subRect.left;
y += client->server->subRect.top;
}
if ((flags & (PTR_FLAGS_WHEEL | PTR_FLAGS_HWHEEL | PTR_FLAGS_WHEEL_NEGATIVE)) == 0)
{
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;
}
}
WLog_DBG(TAG, "[%s] flags=0x%04" PRIx16 ", x=%" PRIu16 ", y=%" PRIu16,
client->mayInteract ? "use" : "discard", flags, x, y);
if (!client->mayInteract)
return TRUE;
return IFCALLRESULT(TRUE, subsystem->MouseEvent, subsystem, client, flags, x, y);
}
WINPR_ATTR_NODISCARD
static BOOL shadow_input_rel_mouse_event(rdpInput* input, UINT16 flags, INT16 xDelta, INT16 yDelta)
{
WINPR_ASSERT(input);
rdpShadowClient* client = (rdpShadowClient*)input->context;
WINPR_ASSERT(client);
WINPR_ASSERT(client->server);
rdpShadowSubsystem* subsystem = client->server->subsystem;
WINPR_ASSERT(subsystem);
WLog_DBG(TAG, "[%s] flags=0x%04" PRIx16 ", x=%" PRId16 ", y=%" PRId16,
client->mayInteract ? "use" : "discard", flags, xDelta, yDelta);
const uint16_t mask = PTR_FLAGS_MOVE | PTR_FLAGS_DOWN | PTR_FLAGS_BUTTON1 | PTR_FLAGS_BUTTON2 |
PTR_FLAGS_BUTTON3 | PTR_XFLAGS_BUTTON1 | PTR_XFLAGS_BUTTON2;
if ((flags & mask) != 0)
{
WLog_WARN(TAG, "Unknown flags 0x%04x", WINPR_CXX_COMPAT_CAST(unsigned, flags & ~mask));
}
if (!client->mayInteract)
return TRUE;
return IFCALLRESULT(TRUE, subsystem->RelMouseEvent, subsystem, client, flags, xDelta, yDelta);
}
WINPR_ATTR_NODISCARD
static BOOL shadow_input_extended_mouse_event(rdpInput* input, UINT16 flags, UINT16 x, UINT16 y)
{
WINPR_ASSERT(input);
rdpShadowClient* client = (rdpShadowClient*)input->context;
WINPR_ASSERT(client);
WINPR_ASSERT(client->server);
rdpShadowSubsystem* subsystem = client->server->subsystem;
WINPR_ASSERT(subsystem);
if (client->server->shareSubRect)
{
x += client->server->subRect.left;
y += client->server->subRect.top;
}
client->pointerX = x;
client->pointerY = y;
WLog_DBG(TAG, "[%s] flags=0x%04" PRIx16 ", x=%" PRIu16 ", y=%" PRIu16,
client->mayInteract ? "use" : "discard", flags, x, y);
if (!client->mayInteract)
return TRUE;
return IFCALLRESULT(TRUE, subsystem->ExtendedMouseEvent, subsystem, client, flags, x, y);
}
void shadow_input_register_callbacks(rdpInput* input)
{
WINPR_ASSERT(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;
input->RelMouseEvent = shadow_input_rel_mouse_event;
}

View File

@@ -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 */

View File

@@ -0,0 +1,101 @@
/**
* 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 <winpr/cast.h>
#if defined(WITH_RDTK)
#include <rdtk/rdtk.h>
#endif
#include "shadow.h"
#include "shadow_lobby.h"
BOOL shadow_client_init_lobby(rdpShadowServer* server)
{
BOOL rc = FALSE;
RECTANGLE_16 invalidRect = WINPR_C_ARRAY_INIT;
WINPR_ASSERT(server);
rdpShadowSurface* lobby = server->lobby;
if (!lobby)
return FALSE;
#if defined(WITH_RDTK)
rdtkEngine* engine = rdtk_engine_new();
if (!engine)
return FALSE;
EnterCriticalSection(&lobby->lock);
rdtkSurface* surface =
rdtk_surface_new(engine, lobby->data, WINPR_ASSERTING_INT_CAST(uint16_t, lobby->width),
WINPR_ASSERTING_INT_CAST(uint16_t, lobby->height), lobby->scanline);
if (!surface)
goto fail;
#endif
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 */
if (!rectangles_intersection(&invalidRect, &(server->subRect), &invalidRect))
goto fail;
}
#if defined(WITH_RDTK)
const int width = invalidRect.right - invalidRect.left;
const int height = invalidRect.bottom - invalidRect.top;
WINPR_ASSERT(width <= UINT16_MAX);
WINPR_ASSERT(width >= 0);
WINPR_ASSERT(height <= UINT16_MAX);
WINPR_ASSERT(height >= 0);
if (rdtk_surface_fill(surface, invalidRect.left, invalidRect.top, (UINT16)width, (UINT16)height,
0x3BB9FF) < 0)
goto fail;
if (rdtk_label_draw(surface, invalidRect.left, invalidRect.top, (UINT16)width, (UINT16)height,
nullptr, "Welcome", 0, 0) < 0)
goto fail;
// rdtk_button_draw(surface, 16, 64, 128, 32, nullptr, "button");
// rdtk_text_field_draw(surface, 16, 128, 128, 32, nullptr, "text field");
#endif
if (!region16_union_rect(&(lobby->invalidRegion), &(lobby->invalidRegion), &invalidRect))
goto fail;
rc = TRUE;
fail:
#if defined(WITH_RDTK)
rdtk_surface_free(surface);
rdtk_engine_free(engine);
#endif
LeaveCriticalSection(&lobby->lock);
return rc;
}

View File

@@ -0,0 +1,38 @@
/**
* 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>
#ifdef __cplusplus
extern "C"
{
#endif
WINPR_ATTR_NODISCARD BOOL shadow_client_init_lobby(rdpShadowServer* server);
#ifdef __cplusplus
}
#endif
#endif /* FREERDP_SERVER_SHADOW_LOBBY_H */

View File

@@ -0,0 +1,341 @@
/**
* 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(nullptr, TRUE, FALSE, nullptr);
if (!event->event)
goto out_free;
event->barrierEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr);
if (!event->barrierEvent)
goto out_free_event;
event->doneEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr);
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;
(void)SetEvent(event->doneEvent);
return event;
out_free_subscribers:
ArrayList_Free(event->subscribers);
out_free_doneEvent:
(void)CloseHandle(event->doneEvent);
out_free_barrierEvent:
(void)CloseHandle(event->barrierEvent);
out_free_event:
(void)CloseHandle(event->event);
out_free:
free(event);
out_error:
return (rdpShadowMultiClientEvent*)nullptr;
}
void shadow_multiclient_free(rdpShadowMultiClientEvent* event)
{
if (!event)
return;
DeleteCriticalSection(&(event->lock));
ArrayList_Free(event->subscribers);
(void)CloseHandle(event->doneEvent);
(void)CloseHandle(event->barrierEvent);
(void)CloseHandle(event->event);
free(event);
}
static void Publish(rdpShadowMultiClientEvent* event)
{
wArrayList* subscribers = nullptr;
struct rdp_shadow_multiclient_subscriber* subscriber = nullptr;
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);
(void)ResetEvent(event->doneEvent);
(void)SetEvent(event->event);
}
}
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));
(void)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);
}
void shadow_multiclient_publish(rdpShadowMultiClientEvent* event)
{
if (!event)
return;
EnterCriticalSection(&(event->lock));
Publish(event);
LeaveCriticalSection(&(event->lock));
}
void shadow_multiclient_wait(rdpShadowMultiClientEvent* event)
{
if (!event)
return;
EnterCriticalSection(&(event->lock));
WaitForSubscribers(event);
LeaveCriticalSection(&(event->lock));
}
void shadow_multiclient_publish_and_wait(rdpShadowMultiClientEvent* event)
{
if (!event)
return;
EnterCriticalSection(&(event->lock));
Publish(event);
WaitForSubscribers(event);
LeaveCriticalSection(&(event->lock));
}
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 */
(void)ResetEvent(event->event);
if (event->waiting > 0)
{
/* Notify other clients to continue */
(void)SetEvent(event->barrierEvent);
}
else
{
/* Only one client. Notify server directly */
(void)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));
(void)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.
*/
(void)ResetEvent(event->barrierEvent);
(void)SetEvent(event->doneEvent);
}
}
}
return ret;
}
void* shadow_multiclient_get_subscriber(rdpShadowMultiClientEvent* event)
{
struct rdp_shadow_multiclient_subscriber* subscriber = nullptr;
if (!event)
return nullptr;
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 nullptr;
}
/*
* 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 = nullptr;
rdpShadowMultiClientEvent* event = nullptr;
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);
}
BOOL shadow_multiclient_consume(void* subscriber)
{
struct rdp_shadow_multiclient_subscriber* s = nullptr;
rdpShadowMultiClientEvent* event = nullptr;
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) nullptr;
return ((struct rdp_shadow_multiclient_subscriber*)subscriber)->ref->event;
}

View File

@@ -0,0 +1,57 @@
/**
* 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)
WINPR_ATTR_NODISCARD
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);
WINPR_ATTR_NODISCARD void* shadow_multiclient_get_subscriber(rdpShadowMultiClientEvent* event);
void shadow_multiclient_release_subscriber(void* subscriber);
BOOL shadow_multiclient_consume(void* subscriber);
WINPR_ATTR_NODISCARD HANDLE shadow_multiclient_getevent(void* subscriber);
#ifdef __cplusplus
}
#endif
#endif /* FREERDP_SERVER_SHADOW_MCEVENT_H */

View File

@@ -0,0 +1,59 @@
/**
* 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"
int shadow_client_rdpgfx_init(rdpShadowClient* client)
{
WINPR_ASSERT(client);
if (!freerdp_settings_get_bool(client->context.settings, FreeRDP_SupportGraphicsPipeline))
return 1;
#if defined(CHANNEL_RDPGFX_SERVER)
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;
#endif
return 1;
}
void shadow_client_rdpgfx_uninit(rdpShadowClient* client)
{
WINPR_ASSERT(client);
if (client->rdpgfx)
{
#if defined(CHANNEL_RDPGFX_SERVER)
rdpgfx_server_context_free(client->rdpgfx);
#endif
client->rdpgfx = nullptr;
}
}

View File

@@ -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
WINPR_ATTR_NODISCARD int shadow_client_rdpgfx_init(rdpShadowClient* client);
void shadow_client_rdpgfx_uninit(rdpShadowClient* client);
#ifdef __cplusplus
}
#endif
#endif /* FREERDP_SERVER_SHADOW_RDPGFX_H */

View File

@@ -0,0 +1,97 @@
/**
* 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 <winpr/assert.h>
#include <winpr/cast.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)
{
WINPR_ASSERT(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]))
{
const UINT rc = context->SelectFormat(context, WINPR_ASSERTING_INT_CAST(UINT16, i));
if (rc != CHANNEL_RC_OK)
WLog_WARN(TAG, "SelectFormat failed with %" PRIu32, rc);
return;
}
}
}
WLog_ERR(TAG, "Could not agree on a audio format with the server\n");
}
int shadow_client_rdpsnd_init(rdpShadowClient* client)
{
WINPR_ASSERT(client);
RdpsndServerContext* 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;
const UINT error = rdpsnd->Initialize(rdpsnd, TRUE);
if (error != CHANNEL_RC_OK)
return -1;
return 1;
}
void shadow_client_rdpsnd_uninit(rdpShadowClient* client)
{
WINPR_ASSERT(client);
if (client->rdpsnd)
{
client->rdpsnd->Stop(client->rdpsnd);
rdpsnd_server_context_free(client->rdpsnd);
client->rdpsnd = nullptr;
}
}

View File

@@ -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
WINPR_ATTR_NODISCARD int shadow_client_rdpsnd_init(rdpShadowClient* client);
void shadow_client_rdpsnd_uninit(rdpShadowClient* client);
#ifdef __cplusplus
}
#endif
#endif /* FREERDP_SERVER_SHADOW_RDPSND_H */

View File

@@ -0,0 +1,57 @@
/**
* 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)
{
WINPR_ASSERT(client);
RemdeskServerContext* remdesk = client->remdesk = remdesk_server_context_new(client->vcm);
if (!remdesk)
return -1;
remdesk->rdpcontext = &client->context;
remdesk->custom = (void*)client;
if (client->remdesk)
{
const UINT rc = client->remdesk->Start(client->remdesk);
if (rc != CHANNEL_RC_OK)
return -1;
}
return 1;
}
void shadow_client_remdesk_uninit(rdpShadowClient* client)
{
WINPR_ASSERT(client);
if (client->remdesk)
{
client->remdesk->Stop(client->remdesk);
remdesk_server_context_free(client->remdesk);
client->remdesk = nullptr;
}
}

View File

@@ -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
WINPR_ATTR_NODISCARD int shadow_client_remdesk_init(rdpShadowClient* client);
void shadow_client_remdesk_uninit(rdpShadowClient* client);
#ifdef __cplusplus
}
#endif
#endif /* FREERDP_SERVER_SHADOW_REMDESK_H */

View File

@@ -0,0 +1,168 @@
/**
* 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);
const INT64 x = primary->left;
const INT64 y = primary->top;
const INT64 width = primary->right - primary->left + 1;
const 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 nullptr;
}
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 = nullptr;
}
if (screen->lobby)
{
shadow_surface_free(screen->lobby);
screen->lobby = nullptr;
}
free(screen);
}
BOOL shadow_screen_resize(rdpShadowScreen* screen)
{
if (!screen)
return FALSE;
WINPR_ASSERT(screen->server);
rdpShadowSubsystem* subsystem = screen->server->subsystem;
WINPR_ASSERT(subsystem);
WINPR_ASSERT(subsystem->monitors);
MONITOR_DEF* primary = &(subsystem->monitors[subsystem->selectedMonitor]);
WINPR_ASSERT(primary);
const INT32 x = primary->left;
const INT32 y = primary->top;
const INT32 width = primary->right - primary->left + 1;
const INT32 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;
return shadow_client_init_lobby(screen->server);
}
return TRUE;
}
return FALSE;
}

View File

@@ -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_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)
WINPR_ATTR_NODISCARD
rdpShadowScreen* shadow_screen_new(rdpShadowServer* server);
#ifdef __cplusplus
}
#endif
#endif /* FREERDP_SERVER_SHADOW_SCREEN_H */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,296 @@
/**
* 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 = nullptr;
void shadow_subsystem_set_entry(pfnShadowSubsystemEntry pEntry)
{
pSubsystemEntry = pEntry;
}
WINPR_ATTR_NODISCARD
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 = WINPR_C_ARRAY_INIT;
if (shadow_subsystem_load_entry_points(&ep) < 0)
return nullptr;
if (!ep.New)
return nullptr;
rdpShadowSubsystem* subsystem = ep.New();
if (!subsystem)
return nullptr;
subsystem->ep = ep;
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;
status = subsystem->ep.Init(subsystem);
if (status >= 0)
return status;
fail:
if (subsystem->MsgPipe)
{
MessagePipe_Free(subsystem->MsgPipe);
subsystem->MsgPipe = nullptr;
}
if (subsystem->updateEvent)
{
shadow_multiclient_free(subsystem->updateEvent);
subsystem->updateEvent = nullptr;
}
return status;
}
static void shadow_subsystem_free_queued_message(void* obj)
{
wMessage* message = (wMessage*)obj;
if (message->Free)
{
message->Free(message);
message->Free = nullptr;
}
}
void shadow_subsystem_uninit(rdpShadowSubsystem* subsystem)
{
if (!subsystem)
return;
if (subsystem->ep.Uninit)
subsystem->ep.Uninit(subsystem);
if (subsystem->MsgPipe)
{
wObject* obj1 = nullptr;
wObject* obj2 = nullptr;
/* 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 = nullptr;
}
if (subsystem->updateEvent)
{
shadow_multiclient_free(subsystem->updateEvent);
subsystem->updateEvent = nullptr;
}
}
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.
*/
#if !defined(WITHOUT_FREERDP_3x_DEPRECATED)
int shadow_subsystem_pointer_convert_alpha_pointer_data(
const BYTE* WINPR_RESTRICT pixels, BOOL premultiplied, UINT32 width, UINT32 height,
SHADOW_MSG_OUT_POINTER_ALPHA_UPDATE* WINPR_RESTRICT pointerColor)
{
return shadow_subsystem_pointer_convert_alpha_pointer_data_to_format(
pixels, PIXEL_FORMAT_BGRX32, premultiplied, width, height, pointerColor);
}
#endif
int shadow_subsystem_pointer_convert_alpha_pointer_data_to_format(
const BYTE* pixels, UINT32 format, BOOL premultiplied, UINT32 width, UINT32 height,
SHADOW_MSG_OUT_POINTER_ALPHA_UPDATE* pointerColor)
{
UINT32 xorStep = 0;
UINT32 andStep = 0;
UINT32 andBit = 0;
BYTE* andBits = nullptr;
UINT32 andPixel = 0;
const size_t bpp = FreeRDPGetBytesPerPixel(format);
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 = nullptr;
return -1;
}
for (size_t y = 0; y < height; y++)
{
const BYTE* pSrc8 = &pixels[(width * bpp) * (height - 1 - y)];
BYTE* pDst8 = &(pointerColor->xorMaskData[y * xorStep]);
andBit = 0x80;
andBits = &(pointerColor->andMaskData[andStep * y]);
for (size_t x = 0; x < width; x++)
{
BYTE B = 0;
BYTE G = 0;
BYTE R = 0;
BYTE A = 0;
const UINT32 color = FreeRDPReadColor(&pSrc8[x * bpp], format);
FreeRDPSplitColor(color, format, &R, &G, &B, &A, nullptr);
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);
}

View File

@@ -0,0 +1,49 @@
/**
* 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)
WINPR_ATTR_NODISCARD
rdpShadowSubsystem* shadow_subsystem_new(void);
WINPR_ATTR_NODISCARD int shadow_subsystem_init(rdpShadowSubsystem* subsystem,
rdpShadowServer* server);
void shadow_subsystem_uninit(rdpShadowSubsystem* subsystem);
WINPR_ATTR_NODISCARD int shadow_subsystem_start(rdpShadowSubsystem* subsystem);
int shadow_subsystem_stop(rdpShadowSubsystem* subsystem);
#ifdef __cplusplus
}
#endif
#endif /* FREERDP_SERVER_SHADOW_SUBSYSTEM_H */

View File

@@ -0,0 +1,74 @@
/**
* 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 const RDP_SHADOW_SUBSYSTEM g_Subsystems[] = {
{ ShadowSubsystemName, ShadowSubsystemEntry }
};
static const size_t g_SubsystemCount = ARRAYSIZE(g_Subsystems);
WINPR_ATTR_NODISCARD
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 nullptr;
}
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 nullptr;
}
void shadow_subsystem_set_entry_builtin(const char* name)
{
pfnShadowSubsystemEntry entry = shadow_subsystem_load_static_entry(name);
if (entry)
shadow_subsystem_set_entry(entry);
}

View File

@@ -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 = nullptr;
surface = (rdpShadowSurface*)calloc(1, sizeof(rdpShadowSurface));
if (!surface)
return nullptr;
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 nullptr;
}
if (!InitializeCriticalSectionAndSpinCount(&(surface->lock), 4000))
{
free(surface->data);
free(surface);
return nullptr;
}
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 = nullptr;
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;
}

View File

@@ -0,0 +1,46 @@
/**
* 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)
WINPR_ATTR_NODISCARD
rdpShadowSurface* shadow_surface_new(rdpShadowServer* server, UINT16 x, UINT16 y, UINT32 width,
UINT32 height);
WINPR_ATTR_NODISCARD BOOL shadow_surface_resize(rdpShadowSurface* surface, UINT16 x, UINT16 y,
UINT32 width, UINT32 height);
#ifdef __cplusplus
}
#endif
#endif /* FREERDP_SERVER_SHADOW_SURFACE_H */