shadow: delay subsystem initialization for monitor enumeration

This commit is contained in:
Marc-André Moreau 2014-09-18 13:06:49 -04:00
parent a77279fb4c
commit 527638c691
10 changed files with 250 additions and 182 deletions

View File

@ -55,6 +55,8 @@ typedef int (*pfnShadowSubsystemStart)(rdpShadowSubsystem* subsystem);
typedef int (*pfnShadowSubsystemStop)(rdpShadowSubsystem* subsystem);
typedef void (*pfnShadowSubsystemFree)(rdpShadowSubsystem* subsystem);
typedef int (*pfnShadowEnumMonitors)(rdpShadowSubsystem* subsystem, MONITOR_DEF* monitors, int maxMonitors);
typedef int (*pfnShadowSurfaceCopy)(rdpShadowSubsystem* subsystem);
typedef int (*pfnShadowSurfaceUpdate)(rdpShadowSubsystem* subsystem, REGION16* subRect);
@ -100,6 +102,7 @@ struct rdp_shadow_server
BOOL mayView;
BOOL mayInteract;
BOOL shareSubRect;
int selectedMonitor;
RECTANGLE_16 subRect;
char* ipcSocket;
char* ConfigPath;
@ -107,12 +110,11 @@ struct rdp_shadow_server
char* PrivateKeyFile;
CRITICAL_SECTION lock;
freerdp_listener* listener;
pfnShadowCreateSubsystem CreateSubsystem;
};
#define RDP_SHADOW_SUBSYSTEM_COMMON() \
HANDLE event; \
int monitorCount; \
int numMonitors; \
int selectedMonitor; \
MONITOR_DEF monitors[16]; \
MONITOR_DEF virtualScreen; \
@ -127,6 +129,7 @@ struct rdp_shadow_server
pfnShadowSubsystemStop Stop; \
pfnShadowSubsystemFree Free; \
\
pfnShadowEnumMonitors EnumMonitors; \
pfnShadowSurfaceCopy SurfaceCopy; \
pfnShadowSurfaceUpdate SurfaceUpdate; \
\
@ -156,6 +159,8 @@ FREERDP_API int shadow_server_stop(rdpShadowServer* server);
FREERDP_API int shadow_server_init(rdpShadowServer* server);
FREERDP_API int shadow_server_uninit(rdpShadowServer* server);
FREERDP_API int shadow_enum_monitors(MONITOR_DEF* monitors, int maxMonitors, UINT32 flags);
FREERDP_API rdpShadowServer* shadow_server_new();
FREERDP_API void shadow_server_free(rdpShadowServer* server);

View File

@ -79,16 +79,16 @@ static const char* inet_ntop(int af, const void* src, char* dst, size_t cnt)
static BOOL freerdp_listener_open(freerdp_listener* instance, const char* bind_address, UINT16 port)
{
rdpListener* listener = (rdpListener*) instance->listener;
int status;
int sockfd;
char servname[10];
struct addrinfo hints = { 0 };
struct addrinfo* res;
struct addrinfo* ai;
int option_value;
char addr[64];
void* sin_addr;
char buf[50];
int option_value;
char servname[16];
struct addrinfo* ai;
struct addrinfo* res;
struct addrinfo hints = { 0 };
rdpListener* listener = (rdpListener*) instance->listener;
#ifdef _WIN32
u_long arg;
#endif
@ -96,7 +96,7 @@ static BOOL freerdp_listener_open(freerdp_listener* instance, const char* bind_a
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
if (bind_address == NULL)
if (!bind_address)
hints.ai_flags = AI_PASSIVE;
sprintf_s(servname, sizeof(servname), "%d", port);
@ -112,9 +112,9 @@ static BOOL freerdp_listener_open(freerdp_listener* instance, const char* bind_a
return FALSE;
}
for (ai = res; ai && listener->num_sockfds < 5; ai = ai->ai_next)
for (ai = res; ai && (listener->num_sockfds < 5); ai = ai->ai_next)
{
if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6)
if ((ai->ai_family != AF_INET) && (ai->ai_family != AF_INET6))
continue;
sockfd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
@ -125,6 +125,16 @@ static BOOL freerdp_listener_open(freerdp_listener* instance, const char* bind_a
continue;
}
if (ai->ai_family == AF_INET)
sin_addr = &(((struct sockaddr_in*) ai->ai_addr)->sin_addr);
else
sin_addr = &(((struct sockaddr_in6*) ai->ai_addr)->sin6_addr);
inet_ntop(ai->ai_family, sin_addr, addr, sizeof(addr));
if (strcmp(addr, "::") == 0)
continue;
option_value = 1;
if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (void*) &option_value, sizeof(option_value)) == -1)
@ -166,12 +176,7 @@ static BOOL freerdp_listener_open(freerdp_listener* instance, const char* bind_a
listener->events[listener->num_sockfds] = CreateFileDescriptorEvent(NULL, FALSE, FALSE, sockfd);
listener->num_sockfds++;
if (ai->ai_family == AF_INET)
sin_addr = &(((struct sockaddr_in*) ai->ai_addr)->sin_addr);
else
sin_addr = &(((struct sockaddr_in6*) ai->ai_addr)->sin6_addr);
WLog_ERR(TAG, "Listening on %s port %s.\n", inet_ntop(ai->ai_family, sin_addr, buf, sizeof(buf)), servname);
WLog_INFO(TAG, "Listening on %s:%s", addr, servname);
}
freeaddrinfo(res);

View File

@ -224,7 +224,7 @@ int mac_shadow_detect_monitors(macShadowSubsystem* subsystem)
subsystem->height = subsystem->pixelHeight;
}
subsystem->monitorCount = 1;
subsystem->numMonitors = 1;
monitor = &(subsystem->monitors[0]);
@ -588,8 +588,6 @@ void mac_shadow_subsystem_free(macShadowSubsystem* subsystem)
mac_shadow_subsystem_uninit(subsystem);
shadow_subsystem_common_free((rdpShadowSubsystem*) subsystem);
free(subsystem);
}
@ -602,9 +600,6 @@ macShadowSubsystem* mac_shadow_subsystem_new(rdpShadowServer* server)
if (!subsystem)
return NULL;
subsystem->server = server;
shadow_subsystem_common_new((rdpShadowSubsystem*) subsystem);
subsystem->Init = (pfnShadowSubsystemInit) mac_shadow_subsystem_init;
subsystem->Uninit = (pfnShadowSubsystemInit) mac_shadow_subsystem_uninit;
subsystem->Start = (pfnShadowSubsystemStart) mac_shadow_subsystem_start;

View File

@ -435,9 +435,9 @@ int win_shadow_subsystem_init(winShadowSubsystem* subsystem)
virtualScreen->bottom = subsystem->height;
virtualScreen->flags = 1;
if (subsystem->monitorCount < 1)
if (subsystem->numMonitors < 1)
{
subsystem->monitorCount = 1;
subsystem->numMonitors = 1;
subsystem->monitors[0].left = virtualScreen->left;
subsystem->monitors[0].top = virtualScreen->top;
subsystem->monitors[0].right = virtualScreen->right;
@ -493,8 +493,6 @@ void win_shadow_subsystem_free(winShadowSubsystem* subsystem)
win_shadow_subsystem_uninit(subsystem);
shadow_subsystem_common_free((rdpShadowSubsystem*) subsystem);
free(subsystem);
}
@ -507,9 +505,6 @@ winShadowSubsystem* win_shadow_subsystem_new(rdpShadowServer* server)
if (!subsystem)
return NULL;
subsystem->server = server;
shadow_subsystem_common_new((rdpShadowSubsystem*) subsystem);
subsystem->Init = (pfnShadowSubsystemInit) win_shadow_subsystem_init;
subsystem->Uninit = (pfnShadowSubsystemInit) win_shadow_subsystem_uninit;
subsystem->Start = (pfnShadowSubsystemStart) win_shadow_subsystem_start;

View File

@ -592,6 +592,36 @@ void* x11_shadow_subsystem_thread(x11ShadowSubsystem* subsystem)
return NULL;
}
int x11_shadow_subsystem_base_init(x11ShadowSubsystem* subsystem)
{
if (subsystem->display)
return 1; /* initialize once */
if (!getenv("DISPLAY"))
setenv("DISPLAY", ":0", 1);
if (!XInitThreads())
return -1;
subsystem->display = XOpenDisplay(NULL);
if (!subsystem->display)
{
WLog_ERR(TAG, "failed to open display: %s", XDisplayName(NULL));
return -1;
}
subsystem->xfds = ConnectionNumber(subsystem->display);
subsystem->number = DefaultScreen(subsystem->display);
subsystem->screen = ScreenOfDisplay(subsystem->display, subsystem->number);
subsystem->depth = DefaultDepthOfScreen(subsystem->screen);
subsystem->width = WidthOfScreen(subsystem->screen);
subsystem->height = HeightOfScreen(subsystem->screen);
subsystem->root_window = DefaultRootWindow(subsystem->display);
return 1;
}
int x11_shadow_xfixes_init(x11ShadowSubsystem* subsystem)
{
#ifdef WITH_XFIXES
@ -618,14 +648,11 @@ int x11_shadow_xfixes_init(x11ShadowSubsystem* subsystem)
int x11_shadow_xinerama_init(x11ShadowSubsystem* subsystem)
{
#ifdef WITH_XINERAMA
int index;
int numMonitors;
int major, minor;
int xinerama_event;
int xinerama_error;
MONITOR_DEF* monitor;
XineramaScreenInfo* screen;
XineramaScreenInfo* screens;
x11_shadow_subsystem_base_init(subsystem);
if (!XineramaQueryExtension(subsystem->display, &xinerama_event, &xinerama_error))
return -1;
@ -636,32 +663,10 @@ int x11_shadow_xinerama_init(x11ShadowSubsystem* subsystem)
if (!XineramaIsActive(subsystem->display))
return -1;
screens = XineramaQueryScreens(subsystem->display, &numMonitors);
if (numMonitors > 16)
numMonitors = 16;
if (!screens || (numMonitors < 1))
return -1;
subsystem->monitorCount = numMonitors;
for (index = 0; index < numMonitors; index++)
{
screen = &screens[index];
monitor = &(subsystem->monitors[index]);
monitor->left = screen->x_org;
monitor->top = screen->y_org;
monitor->right = monitor->left + screen->width;
monitor->bottom = monitor->top + screen->height;
monitor->flags = (index == 0) ? 1 : 0;
}
XFree(screens);
#endif
return 1;
#else
return -1;
#endif
}
int x11_shadow_xdamage_init(x11ShadowSubsystem* subsystem)
@ -776,6 +781,61 @@ int x11_shadow_xshm_init(x11ShadowSubsystem* subsystem)
return 1;
}
int x11_shadow_enum_monitors(x11ShadowSubsystem* subsystem, MONITOR_DEF* monitors, int maxMonitors)
{
int index;
int numMonitors = 0;
MONITOR_DEF* monitor;
#ifdef WITH_XINERAMA
if (x11_shadow_xinerama_init(subsystem) > 0)
{
XineramaScreenInfo* screen;
XineramaScreenInfo* screens;
screens = XineramaQueryScreens(subsystem->display, &numMonitors);
if (numMonitors > maxMonitors)
numMonitors = maxMonitors;
if (screens && (numMonitors > 0))
{
for (index = 0; index < numMonitors; index++)
{
screen = &screens[index];
monitor = &monitors[index];
monitor->left = screen->x_org;
monitor->top = screen->y_org;
monitor->right = monitor->left + screen->width;
monitor->bottom = monitor->top + screen->height;
monitor->flags = (index == 0) ? 1 : 0;
}
}
XFree(screens);
}
#endif
if (numMonitors < 1)
{
index = 0;
numMonitors = 1;
x11_shadow_subsystem_base_init(subsystem);
monitor = &monitors[index];
monitor->left = 0;
monitor->top = 0;
monitor->right = subsystem->width;
monitor->bottom = subsystem->height;
monitor->flags = 1;
}
return numMonitors;
}
int x11_shadow_subsystem_init(x11ShadowSubsystem* subsystem)
{
int i;
@ -790,27 +850,9 @@ int x11_shadow_subsystem_init(x11ShadowSubsystem* subsystem)
XPixmapFormatValues* pfs;
MONITOR_DEF* virtualScreen;
/**
* To see if your X11 server supports shared pixmaps, use:
* xdpyinfo -ext MIT-SHM | grep "shared pixmaps"
*/
x11_shadow_subsystem_base_init(subsystem);
if (!getenv("DISPLAY"))
{
/* Set DISPLAY variable if not already set */
setenv("DISPLAY", ":0", 1);
}
if (!XInitThreads())
return -1;
subsystem->display = XOpenDisplay(NULL);
if (!subsystem->display)
{
WLog_ERR(TAG, "failed to open display: %s", XDisplayName(NULL));
return -1;
}
subsystem->numMonitors = x11_shadow_enum_monitors(subsystem, subsystem->monitors, 16);
extensions = XListExtensions(subsystem->display, &nextensions);
@ -828,14 +870,6 @@ int x11_shadow_subsystem_init(x11ShadowSubsystem* subsystem)
if (subsystem->composite)
subsystem->use_xdamage = FALSE;
subsystem->xfds = ConnectionNumber(subsystem->display);
subsystem->number = DefaultScreen(subsystem->display);
subsystem->screen = ScreenOfDisplay(subsystem->display, subsystem->number);
subsystem->depth = DefaultDepthOfScreen(subsystem->screen);
subsystem->width = WidthOfScreen(subsystem->screen);
subsystem->height = HeightOfScreen(subsystem->screen);
subsystem->root_window = DefaultRootWindow(subsystem->display);
pfs = XListPixmapFormats(subsystem->display, &pf_count);
if (!pfs)
@ -926,9 +960,9 @@ int x11_shadow_subsystem_init(x11ShadowSubsystem* subsystem)
virtualScreen->bottom = subsystem->height;
virtualScreen->flags = 1;
if (subsystem->monitorCount < 1)
if (subsystem->numMonitors < 1)
{
subsystem->monitorCount = 1;
subsystem->numMonitors = 1;
subsystem->monitors[0].left = virtualScreen->left;
subsystem->monitors[0].top = virtualScreen->top;
subsystem->monitors[0].right = virtualScreen->right;
@ -997,8 +1031,6 @@ void x11_shadow_subsystem_free(x11ShadowSubsystem* subsystem)
x11_shadow_subsystem_uninit(subsystem);
shadow_subsystem_common_free((rdpShadowSubsystem*) subsystem);
free(subsystem);
}
@ -1011,15 +1043,14 @@ x11ShadowSubsystem* x11_shadow_subsystem_new(rdpShadowServer* server)
if (!subsystem)
return NULL;
subsystem->server = server;
shadow_subsystem_common_new((rdpShadowSubsystem*) subsystem);
subsystem->Init = (pfnShadowSubsystemInit) x11_shadow_subsystem_init;
subsystem->Uninit = (pfnShadowSubsystemInit) x11_shadow_subsystem_uninit;
subsystem->Start = (pfnShadowSubsystemStart) x11_shadow_subsystem_start;
subsystem->Stop = (pfnShadowSubsystemStop) x11_shadow_subsystem_stop;
subsystem->Free = (pfnShadowSubsystemFree) x11_shadow_subsystem_free;
subsystem->EnumMonitors = (pfnShadowEnumMonitors) x11_shadow_enum_monitors;
subsystem->SynchronizeEvent = (pfnShadowSynchronizeEvent) x11_shadow_input_synchronize_event;
subsystem->KeyboardEvent = (pfnShadowKeyboardEvent) x11_shadow_input_keyboard_event;
subsystem->UnicodeKeyboardEvent = (pfnShadowUnicodeKeyboardEvent) x11_shadow_input_unicode_keyboard_event;

View File

@ -53,9 +53,6 @@ int main(int argc, char** argv)
if (!server)
return 0;
if (shadow_server_init(server) < 0)
return 0;
status = shadow_server_parse_command_line(server, argc, argv);
status = shadow_server_command_line_status_print(server, argc, argv, status);
@ -63,6 +60,9 @@ int main(int argc, char** argv)
if (status < 0)
return 0;
if (shadow_server_init(server) < 0)
return 0;
if (shadow_server_start(server) < 0)
return 0;

View File

@ -28,6 +28,7 @@
#include "shadow_encoder.h"
#include "shadow_capture.h"
#include "shadow_channels.h"
#include "shadow_subsystem.h"
#ifdef __cplusplus
extern "C" {

View File

@ -41,18 +41,6 @@
#define TAG SERVER_TAG("shadow")
#ifdef WITH_SHADOW_X11
extern rdpShadowSubsystem* X11_ShadowCreateSubsystem(rdpShadowServer* server);
#endif
#ifdef WITH_SHADOW_MAC
extern rdpShadowSubsystem* Mac_ShadowCreateSubsystem(rdpShadowServer* server);
#endif
#ifdef WITH_SHADOW_WIN
extern rdpShadowSubsystem* Win_ShadowCreateSubsystem(rdpShadowServer* server);
#endif
static COMMAND_LINE_ARGUMENT_A shadow_args[] =
{
{ "port", COMMAND_LINE_VALUE_REQUIRED, "<number>", NULL, NULL, -1, NULL, "Server port" },
@ -258,7 +246,10 @@ int shadow_server_parse_command_line(rdpShadowServer* server, int argc, char** a
if (arg && (arg->Flags & COMMAND_LINE_ARGUMENT_PRESENT))
{
int index;
rdpShadowSubsystem* subsystem = server->subsystem;
int numMonitors;
MONITOR_DEF monitors[16];
numMonitors = shadow_enum_monitors(monitors, 16, 0);
if (arg->Flags & COMMAND_LINE_VALUE_PRESENT)
{
@ -269,10 +260,10 @@ int shadow_server_parse_command_line(rdpShadowServer* server, int argc, char** a
if (index < 0)
index = 0;
if (index >= subsystem->monitorCount)
if (index >= numMonitors)
index = 0;
subsystem->selectedMonitor = index;
server->selectedMonitor = index;
}
else
{
@ -281,9 +272,9 @@ int shadow_server_parse_command_line(rdpShadowServer* server, int argc, char** a
/* List monitors */
for (index = 0; index < subsystem->monitorCount; index++)
for (index = 0; index < numMonitors; index++)
{
monitor = &(subsystem->monitors[index]);
monitor = &monitors[index];
width = monitor->right - monitor->left;
height = monitor->bottom - monitor->top;
@ -300,32 +291,6 @@ int shadow_server_parse_command_line(rdpShadowServer* server, int argc, char** a
return status;
}
int shadow_server_surface_update(rdpShadowSubsystem* subsystem, REGION16* region)
{
int index;
int count;
wArrayList* clients;
rdpShadowServer* server;
rdpShadowClient* client;
server = subsystem->server;
clients = server->clients;
ArrayList_Lock(clients);
count = ArrayList_Count(clients);
for (index = 0; index < count; index++)
{
client = ArrayList_GetItem(clients, index);
shadow_client_surface_update(client, region);
}
ArrayList_Unlock(clients);
return 1;
}
void* shadow_server_thread(rdpShadowServer* server)
{
DWORD status;
@ -571,8 +536,17 @@ int shadow_server_init(rdpShadowServer* server)
WTSRegisterWtsApiFunctionTable(FreeRDP_InitWtsApi());
server->clients = ArrayList_New(TRUE);
server->StopEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
InitializeCriticalSectionAndSpinCount(&(server->lock), 4000);
status = shadow_server_init_config_path(server);
if (status < 0)
return -1;
status = shadow_server_init_certificate(server);
if (status < 0)
@ -586,35 +560,14 @@ int shadow_server_init(rdpShadowServer* server)
server->listener->info = (void*) server;
server->listener->PeerAccepted = shadow_client_accepted;
#ifdef WITH_SHADOW_X11
server->CreateSubsystem = X11_ShadowCreateSubsystem;
#endif
#ifdef WITH_SHADOW_MAC
server->CreateSubsystem = Mac_ShadowCreateSubsystem;
#endif
#ifdef WITH_SHADOW_WIN
server->CreateSubsystem = Win_ShadowCreateSubsystem;
#endif
if (server->CreateSubsystem)
server->subsystem = server->CreateSubsystem(server);
server->subsystem = shadow_subsystem_new(0);
if (!server->subsystem)
return -1;
server->subsystem->SurfaceUpdate = shadow_server_surface_update;
status = shadow_subsystem_init(server->subsystem, server);
if (server->subsystem->Init)
{
status = server->subsystem->Init(server->subsystem);
if (status < 0)
WLog_ERR(TAG, "subsystem init failure: %d", status);
}
return 1;
return status;
}
int shadow_server_uninit(rdpShadowServer* server)
@ -651,9 +604,31 @@ int shadow_server_uninit(rdpShadowServer* server)
server->ipcSocket = NULL;
}
shadow_subsystem_uninit(server->subsystem);
return 1;
}
int shadow_enum_monitors(MONITOR_DEF* monitors, int maxMonitors, UINT32 flags)
{
int numMonitors = 0;
rdpShadowSubsystem* subsystem;
subsystem = shadow_subsystem_new(flags);
if (!subsystem)
return -1;
if (!subsystem->EnumMonitors)
return -1;
numMonitors = subsystem->EnumMonitors(subsystem, monitors, maxMonitors);
shadow_subsystem_free(subsystem);
return numMonitors;
}
rdpShadowServer* shadow_server_new()
{
rdpShadowServer* server;
@ -667,12 +642,6 @@ rdpShadowServer* shadow_server_new()
server->mayView = TRUE;
server->mayInteract = TRUE;
shadow_server_init_config_path(server);
InitializeCriticalSectionAndSpinCount(&(server->lock), 4000);
server->clients = ArrayList_New(TRUE);
return server;
}

View File

@ -24,22 +24,86 @@
#include "shadow_subsystem.h"
int shadow_subsystem_common_new(rdpShadowSubsystem* subsystem)
#ifdef WITH_SHADOW_X11
extern rdpShadowSubsystem* X11_ShadowCreateSubsystem(rdpShadowServer* server);
#endif
#ifdef WITH_SHADOW_MAC
extern rdpShadowSubsystem* Mac_ShadowCreateSubsystem(rdpShadowServer* server);
#endif
#ifdef WITH_SHADOW_WIN
extern rdpShadowSubsystem* Win_ShadowCreateSubsystem(rdpShadowServer* server);
#endif
rdpShadowSubsystem* shadow_subsystem_new(UINT32 flags)
{
rdpShadowSubsystem* subsystem = NULL;
pfnShadowCreateSubsystem CreateSubsystem = NULL;
#ifdef WITH_SHADOW_X11
CreateSubsystem = X11_ShadowCreateSubsystem;
#endif
#ifdef WITH_SHADOW_MAC
CreateSubsystem = Mac_ShadowCreateSubsystem;
#endif
#ifdef WITH_SHADOW_WIN
CreateSubsystem = Win_ShadowCreateSubsystem;
#endif
if (CreateSubsystem)
subsystem = CreateSubsystem(NULL);
return subsystem;
}
void shadow_subsystem_free(rdpShadowSubsystem* subsystem)
{
if (subsystem->Free)
subsystem->Free(subsystem);
}
int shadow_subsystem_init(rdpShadowSubsystem* subsystem, rdpShadowServer* server)
{
int status;
subsystem->server = server;
subsystem->selectedMonitor = server->selectedMonitor;
subsystem->updateEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
subsystem->MsgPipe = MessagePipe_New();
region16_init(&(subsystem->invalidRegion));
return 1;
if (!subsystem->Init)
return -1;
if (subsystem->Init)
status = subsystem->Init(subsystem);
return status;
}
void shadow_subsystem_common_free(rdpShadowSubsystem* subsystem)
void shadow_subsystem_uninit(rdpShadowSubsystem* subsystem)
{
CloseHandle(subsystem->updateEvent);
if (subsystem->Uninit)
subsystem->Uninit(subsystem);
MessagePipe_Free(subsystem->MsgPipe);
if (subsystem->updateEvent)
{
CloseHandle(subsystem->updateEvent);
subsystem->updateEvent = NULL;
}
region16_uninit(&(subsystem->invalidRegion));
if (subsystem->MsgPipe)
{
MessagePipe_Free(subsystem->MsgPipe);
subsystem->MsgPipe = NULL;
}
if (subsystem->invalidRegion.data)
{
region16_uninit(&(subsystem->invalidRegion));
}
}

View File

@ -30,8 +30,11 @@
extern "C" {
#endif
int shadow_subsystem_common_new(rdpShadowSubsystem* subsystem);
void shadow_subsystem_common_free(rdpShadowSubsystem* subsystem);
rdpShadowSubsystem* shadow_subsystem_new(UINT32 flags);
void shadow_subsystem_free(rdpShadowSubsystem* subsystem);
int shadow_subsystem_init(rdpShadowSubsystem* subsystem, rdpShadowServer* server);
void shadow_subsystem_uninit(rdpShadowSubsystem* subsystem);
#ifdef __cplusplus
}