shadow: initial X11 multi-monitor support

This commit is contained in:
Marc-André Moreau 2014-07-14 13:33:20 -04:00
parent 8c9434f0dc
commit f0ce0b8148
9 changed files with 419 additions and 73 deletions

View File

@ -85,6 +85,7 @@ struct rdp_shadow_server
HANDLE event; \
int monitorCount; \
MONITOR_DEF monitors[16]; \
MONITOR_DEF virtualScreen; \
\
pfnShadowSubsystemInit Init; \
pfnShadowSubsystemUninit Uninit; \

View File

@ -37,6 +37,7 @@
#include <freerdp/codec/color.h>
#include <freerdp/codec/region.h>
#include "../shadow_screen.h"
#include "../shadow_surface.h"
#include "x11_shadow.h"
@ -152,6 +153,9 @@ void x11_shadow_validate_region(x11ShadowSubsystem* subsystem, int x, int y, int
{
XRectangle region;
if (!subsystem->use_xfixes)
return;
region.x = x;
region.y = y;
region.width = width;
@ -166,20 +170,20 @@ void x11_shadow_validate_region(x11ShadowSubsystem* subsystem, int x, int y, int
int x11_shadow_invalidate_region(x11ShadowSubsystem* subsystem, int x, int y, int width, int height)
{
rdpShadowServer* server;
rdpShadowSurface* surface;
rdpShadowScreen* screen;
RECTANGLE_16 invalidRect;
server = subsystem->server;
surface = server->surface;
screen = server->screen;
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));
EnterCriticalSection(&(screen->lock));
region16_union_rect(&(screen->invalidRegion), &(screen->invalidRegion), &invalidRect);
LeaveCriticalSection(&(screen->lock));
return 1;
}
@ -190,12 +194,23 @@ int x11_shadow_surface_copy(x11ShadowSubsystem* subsystem)
int width;
int height;
XImage* image;
rdpShadowScreen* screen;
rdpShadowServer* server;
rdpShadowSurface* surface;
RECTANGLE_16 surfaceRect;
const RECTANGLE_16* extents;
server = subsystem->server;
surface = server->surface;
screen = server->screen;
surfaceRect.left = surface->x;
surfaceRect.top = surface->y;
surfaceRect.right = surface->x + surface->width;
surfaceRect.bottom = surface->y + surface->height;
region16_clear(&(surface->invalidRegion));
region16_intersect_rect(&(surface->invalidRegion), &(screen->invalidRegion), &surfaceRect);
if (region16_is_empty(&(surface->invalidRegion)))
return 1;
@ -219,7 +234,7 @@ int x11_shadow_surface_copy(x11ShadowSubsystem* subsystem)
image = subsystem->fb_image;
freerdp_image_copy(surface->data, PIXEL_FORMAT_XRGB32,
surface->scanline, x, y, width, height,
surface->scanline, x - surface->x, y - surface->y, width, height,
(BYTE*) image->data, PIXEL_FORMAT_XRGB32,
image->bytes_per_line, x, y);
}
@ -229,7 +244,7 @@ int x11_shadow_surface_copy(x11ShadowSubsystem* subsystem)
x, y, width, height, AllPlanes, ZPixmap);
freerdp_image_copy(surface->data, PIXEL_FORMAT_XRGB32,
surface->scanline, x, y, width, height,
surface->scanline, x - surface->x, y - surface->y, width, height,
(BYTE*) image->data, PIXEL_FORMAT_XRGB32,
image->bytes_per_line, 0, 0);
@ -291,29 +306,87 @@ void* x11_shadow_subsystem_thread(x11ShadowSubsystem* subsystem)
return NULL;
}
int x11_shadow_cursor_init(x11ShadowSubsystem* subsystem)
int x11_shadow_xfixes_init(x11ShadowSubsystem* subsystem)
{
#ifdef WITH_XFIXES
int event;
int error;
int xfixes_event;
int xfixes_error;
int major, minor;
if (!XFixesQueryExtension(subsystem->display, &event, &error))
if (!XFixesQueryExtension(subsystem->display, &xfixes_event, &xfixes_error))
return -1;
subsystem->xfixes_notify_event = event + XFixesCursorNotify;
if (!XFixesQueryVersion(subsystem->display, &major, &minor))
return -1;
subsystem->xfixes_notify_event = xfixes_event + XFixesCursorNotify;
XFixesSelectCursorInput(subsystem->display, DefaultRootWindow(subsystem->display), XFixesDisplayCursorNotifyMask);
return 1;
#else
return -1;
#endif
}
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;
if (!XineramaQueryExtension(subsystem->display, &xinerama_event, &xinerama_error))
return -1;
if (!XDamageQueryVersion(subsystem->display, &major, &minor))
return -1;
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 0;
return 1;
}
int x11_shadow_xdamage_init(x11ShadowSubsystem* subsystem)
{
#ifdef WITH_XDAMAGE
int major, minor;
int damage_event;
int damage_error;
int major, minor;
if (!subsystem->use_xfixes)
return -1;
if (!XDamageQueryExtension(subsystem->display, &damage_event, &damage_error))
return -1;
@ -396,9 +469,6 @@ int x11_shadow_xshm_init(x11ShadowSubsystem* subsystem)
shmctl(subsystem->fb_shm_info.shmid, IPC_RMID, 0);
fprintf(stderr, "display: %p root_window: %p width: %d height: %d depth: %d\n",
subsystem->display, (void*) subsystem->root_window, subsystem->fb_image->width, subsystem->fb_image->height, subsystem->fb_image->depth);
subsystem->fb_pixmap = XShmCreatePixmap(subsystem->display,
subsystem->root_window, subsystem->fb_image->data, &(subsystem->fb_shm_info),
subsystem->fb_image->width, subsystem->fb_image->height, subsystem->fb_image->depth);
@ -430,15 +500,13 @@ int x11_shadow_subsystem_init(x11ShadowSubsystem* subsystem)
XVisualInfo template;
XPixmapFormatValues* pf;
XPixmapFormatValues* pfs;
MONITOR_DEF* virtualScreen;
/**
* To see if your X11 server supports shared pixmaps, use:
* xdpyinfo -ext MIT-SHM | grep "shared pixmaps"
*/
subsystem->use_xshm = TRUE;
subsystem->use_xdamage = TRUE;
if (!getenv("DISPLAY"))
{
/* Set DISPLAY variable if not already set */
@ -511,6 +579,18 @@ int x11_shadow_subsystem_init(x11ShadowSubsystem* subsystem)
XSelectInput(subsystem->display, subsystem->root_window, SubstructureNotifyMask);
if (subsystem->use_xfixes)
{
if (x11_shadow_xfixes_init(subsystem) < 0)
subsystem->use_xfixes = FALSE;
}
if (subsystem->use_xinerama)
{
if (x11_shadow_xinerama_init(subsystem) < 0)
subsystem->use_xinerama = FALSE;
}
if (subsystem->use_xshm)
{
if (x11_shadow_xshm_init(subsystem) < 0)
@ -523,19 +603,28 @@ int x11_shadow_subsystem_init(x11ShadowSubsystem* subsystem)
subsystem->use_xdamage = FALSE;
}
x11_shadow_cursor_init(subsystem);
subsystem->event = CreateFileDescriptorEvent(NULL, FALSE, FALSE, subsystem->xfds);
subsystem->monitorCount = 1;
subsystem->monitors[0].left = 0;
subsystem->monitors[0].top = 0;
subsystem->monitors[0].right = subsystem->width;
subsystem->monitors[0].bottom = subsystem->height;
subsystem->monitors[0].flags = 1;
virtualScreen = &(subsystem->virtualScreen);
if (subsystem->use_xshm)
printf("Using X Shared Memory Extension (XShm)\n");
virtualScreen->left = 0;
virtualScreen->top = 0;
virtualScreen->right = subsystem->width;
virtualScreen->bottom = subsystem->height;
virtualScreen->flags = 1;
if (subsystem->monitorCount < 1)
{
subsystem->monitorCount = 1;
subsystem->monitors[0].left = virtualScreen->left;
subsystem->monitors[0].top = virtualScreen->top;
subsystem->monitors[0].right = virtualScreen->right;
subsystem->monitors[0].bottom = virtualScreen->bottom;
subsystem->monitors[0].flags = 1;
}
printf("X11 Extensions: XFixes: %d Xinerama: %d XDamage: %d XShm: %d\n",
subsystem->use_xfixes, subsystem->use_xinerama, subsystem->use_xdamage, subsystem->use_xshm);
return 1;
}
@ -617,6 +706,11 @@ x11ShadowSubsystem* x11_shadow_subsystem_new(rdpShadowServer* server)
subsystem->MouseEvent = (pfnShadowMouseEvent) x11_shadow_input_mouse_event;
subsystem->ExtendedMouseEvent = (pfnShadowExtendedMouseEvent) x11_shadow_input_extended_mouse_event;
subsystem->use_xshm = TRUE;
subsystem->use_xfixes = TRUE;
subsystem->use_xdamage = TRUE;
subsystem->use_xinerama = TRUE;
return subsystem;
}

View File

@ -47,6 +47,10 @@ typedef struct x11_shadow_subsystem x11ShadowSubsystem;
#include <X11/extensions/Xdamage.h>
#endif
#ifdef WITH_XINERAMA
#include <X11/extensions/Xinerama.h>
#endif
struct x11_shadow_subsystem
{
RDP_SHADOW_SUBSYSTEM_COMMON();
@ -66,7 +70,10 @@ struct x11_shadow_subsystem
int scanline_pad;
BOOL use_xshm;
BOOL use_xfixes;
BOOL use_xdamage;
BOOL use_xinerama;
XImage* fb_image;
Pixmap fb_pixmap;
Window root_window;

View File

@ -20,6 +20,11 @@
#include "config.h"
#endif
#include <winpr/crt.h>
#include <winpr/cmdline.h>
#include <freerdp/version.h>
#ifndef _WIN32
#include <sys/select.h>
#include <sys/signal.h>
@ -35,6 +40,175 @@
extern rdpShadowSubsystem* X11_ShadowCreateSubsystem(rdpShadowServer* server);
#endif
static COMMAND_LINE_ARGUMENT_A shadow_args[] =
{
{ "port", COMMAND_LINE_VALUE_REQUIRED, "<number>", NULL, NULL, -1, NULL, "Server port" },
{ "monitors", COMMAND_LINE_VALUE_OPTIONAL, "<0,1,2...>", NULL, NULL, -1, NULL, "Select or list monitors" },
{ "version", COMMAND_LINE_VALUE_FLAG | COMMAND_LINE_PRINT_VERSION, NULL, NULL, NULL, -1, NULL, "Print version" },
{ "help", COMMAND_LINE_VALUE_FLAG | COMMAND_LINE_PRINT_HELP, NULL, NULL, NULL, -1, "?", "Print help" },
{ NULL, 0, NULL, NULL, NULL, -1, NULL, NULL }
};
int shadow_server_print_command_line_help(int argc, char** argv)
{
char* str;
int length;
COMMAND_LINE_ARGUMENT_A* arg;
printf("Usage: %s [options]\n", argv[0]);
printf("\n");
printf("Syntax:\n");
printf(" /flag (enables flag)\n");
printf(" /option:<value> (specifies option with value)\n");
printf(" +toggle -toggle (enables or disables toggle, where '/' is a synonym of '+')\n");
printf("\n");
arg = shadow_args;
do
{
if (arg->Flags & COMMAND_LINE_VALUE_FLAG)
{
printf(" %s", "/");
printf("%-20s", arg->Name);
printf("\t%s\n", arg->Text);
}
else if ((arg->Flags & COMMAND_LINE_VALUE_REQUIRED) || (arg->Flags & COMMAND_LINE_VALUE_OPTIONAL))
{
printf(" %s", "/");
if (arg->Format)
{
length = (int) (strlen(arg->Name) + strlen(arg->Format) + 2);
str = (char*) malloc(length + 1);
sprintf_s(str, length + 1, "%s:%s", arg->Name, arg->Format);
printf("%-20s", str);
free(str);
}
else
{
printf("%-20s", arg->Name);
}
printf("\t%s\n", arg->Text);
}
else if (arg->Flags & COMMAND_LINE_VALUE_BOOL)
{
length = (int) strlen(arg->Name) + 32;
str = (char*) malloc(length + 1);
sprintf_s(str, length + 1, "%s (default:%s)", arg->Name,
arg->Default ? "on" : "off");
printf(" %s", arg->Default ? "-" : "+");
printf("%-20s", str);
free(str);
printf("\t%s\n", arg->Text);
}
}
while ((arg = CommandLineFindNextArgumentA(arg)) != NULL);
return 1;
}
int shadow_server_command_line_status_print(rdpShadowServer* server, int argc, char** argv, int status)
{
if (status == COMMAND_LINE_STATUS_PRINT_VERSION)
{
printf("FreeRDP version %s (git %s)\n", FREERDP_VERSION_FULL, GIT_REVISION);
return COMMAND_LINE_STATUS_PRINT_VERSION;
}
else if (status == COMMAND_LINE_STATUS_PRINT)
{
return COMMAND_LINE_STATUS_PRINT;
}
else if (status < 0)
{
shadow_server_print_command_line_help(argc, argv);
return COMMAND_LINE_STATUS_PRINT_HELP;
}
return 1;
}
int shadow_server_parse_command_line(rdpShadowServer* server, int argc, char** argv)
{
int status;
DWORD flags;
COMMAND_LINE_ARGUMENT_A* arg;
if (argc < 2)
return 1;
CommandLineClearArgumentsA(shadow_args);
flags = COMMAND_LINE_SEPARATOR_COLON;
flags |= COMMAND_LINE_SIGIL_SLASH | COMMAND_LINE_SIGIL_PLUS_MINUS;
status = CommandLineParseArgumentsA(argc, (const char**) argv, shadow_args, flags, server, NULL, NULL);
if (status < 0)
return status;
arg = shadow_args;
do
{
if (!(arg->Flags & COMMAND_LINE_ARGUMENT_PRESENT))
continue;
CommandLineSwitchStart(arg)
CommandLineSwitchCase(arg, "port")
{
server->port = (DWORD) atoi(arg->Value);
}
CommandLineSwitchDefault(arg)
{
}
CommandLineSwitchEnd(arg)
}
while ((arg = CommandLineFindNextArgumentA(arg)) != NULL);
arg = CommandLineFindArgumentA(shadow_args, "monitors");
if (arg)
{
if (arg->Flags & COMMAND_LINE_VALUE_PRESENT)
{
/* Select monitors */
}
else
{
int index;
int width, height;
MONITOR_DEF* monitor;
rdpShadowSubsystem* subsystem = server->subsystem;
/* List monitors */
for (index = 0; index < subsystem->monitorCount; index++)
{
monitor = &(subsystem->monitors[index]);
width = monitor->right - monitor->left;
height = monitor->bottom - monitor->top;
printf(" %s [%d] %dx%d\t+%d+%d\n",
(monitor->flags == 1) ? "*" : " ", index,
width, height, monitor->left, monitor->top);
}
status = COMMAND_LINE_STATUS_PRINT;
}
}
return status;
}
void* shadow_server_thread(rdpShadowServer* server)
{
DWORD status;
@ -121,23 +295,14 @@ int shadow_server_stop(rdpShadowServer* server)
return 0;
}
rdpShadowServer* shadow_server_new(int argc, char** argv)
int shadow_server_init(rdpShadowServer* server)
{
rdpShadowServer* server;
server = (rdpShadowServer*) calloc(1, sizeof(rdpShadowServer));
if (!server)
return NULL;
server->port = 3389;
server->StopEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
server->listener = freerdp_listener_new();
if (!server->listener)
return NULL;
return -1;
server->listener->info = (void*) server;
server->listener->PeerAccepted = shadow_client_accepted;
@ -150,18 +315,60 @@ rdpShadowServer* shadow_server_new(int argc, char** argv)
server->subsystem = server->CreateSubsystem(server);
if (!server->subsystem)
return NULL;
return -1;
if (server->subsystem->Init)
server->subsystem->Init(server->subsystem);
server->screen = shadow_screen_new(server);
if (!server->screen)
return NULL;
return -1;
server->encoder = shadow_encoder_new(server);
if (!server->encoder)
return -1;
return 1;
}
int shadow_server_uninit(rdpShadowServer* server)
{
shadow_server_stop(server);
if (server->listener)
{
freerdp_listener_free(server->listener);
server->listener = NULL;
}
if (server->encoder)
{
shadow_encoder_free(server->encoder);
server->encoder = NULL;
}
if (server->subsystem)
{
server->subsystem->Free(server->subsystem);
server->subsystem = NULL;
}
return 1;
}
rdpShadowServer* shadow_server_new()
{
rdpShadowServer* server;
server = (rdpShadowServer*) calloc(1, sizeof(rdpShadowServer));
if (!server)
return NULL;
server->port = 3389;
return server;
}
@ -170,28 +377,34 @@ void shadow_server_free(rdpShadowServer* server)
if (!server)
return;
shadow_server_stop(server);
freerdp_listener_free(server->listener);
shadow_encoder_free(server->encoder);
server->subsystem->Free(server->subsystem);
shadow_server_uninit(server);
free(server);
}
int main(int argc, char* argv[])
{
int status;
DWORD dwExitCode;
rdpShadowServer* server;
server = shadow_server_new(argc, argv);
server = shadow_server_new();
if (!server)
return 0;
shadow_server_start(server);
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);
if (status < 0)
return 0;
if (shadow_server_start(server) < 0)
return 0;
WaitForSingleObject(server->thread, INFINITE);

View File

@ -61,15 +61,18 @@ BOOL shadow_client_capabilities(freerdp_peer* peer)
BOOL shadow_client_post_connect(freerdp_peer* peer)
{
rdpSettings* settings;
rdpShadowClient* client;
client = (rdpShadowClient*) peer->context;
settings = peer->settings;
fprintf(stderr, "Client from %s is activated\n", peer->hostname);
settings->DesktopWidth = client->server->screen->width;
settings->DesktopHeight = client->server->screen->height;
settings->ColorDepth = 32;
peer->settings->DesktopWidth = client->server->screen->width;
peer->settings->DesktopHeight = client->server->screen->height;
peer->settings->ColorDepth = 32;
fprintf(stderr, "Client from %s is activated (%dx%d@%d)\n",
peer->hostname, settings->DesktopWidth, settings->DesktopHeight, settings->ColorDepth);
peer->update->DesktopResize(peer->update->context);
@ -151,10 +154,10 @@ int shadow_client_send_surface_bits(rdpShadowClient* client)
encoder = server->encoder;
surface = server->surface;
surfaceRect.left = 0;
surfaceRect.top = 0;
surfaceRect.right = surface->width;
surfaceRect.bottom = surface->height;
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);
@ -163,8 +166,8 @@ int shadow_client_send_surface_bits(rdpShadowClient* client)
extents = region16_extents(&(surface->invalidRegion));
nXSrc = extents->left;
nYSrc = extents->top;
nXSrc = extents->left - surface->x;
nYSrc = extents->top - surface->y;
nWidth = extents->right - extents->left;
nHeight = extents->bottom - extents->top;
pSrcData = surface->data;
@ -324,13 +327,13 @@ void* shadow_client_thread(rdpShadowClient* client)
freerdp_peer* peer;
rdpSettings* settings;
rdpShadowServer* server;
rdpShadowSurface* surface;
rdpShadowScreen* screen;
rdpShadowEncoder* encoder;
rdpShadowSubsystem* subsystem;
server = client->server;
screen = server->screen;
encoder = server->encoder;
surface = server->surface;
subsystem = server->subsystem;
peer = ((rdpContext*) client)->peer;
@ -393,14 +396,15 @@ void* shadow_client_thread(rdpShadowClient* client)
{
if (client->activated)
{
EnterCriticalSection(&(surface->lock));
EnterCriticalSection(&(screen->lock));
if (subsystem->SurfaceCopy)
subsystem->SurfaceCopy(subsystem);
shadow_client_send_surface_bits(client);
region16_clear(&(surface->invalidRegion));
LeaveCriticalSection(&(surface->lock));
region16_clear(&(screen->invalidRegion));
LeaveCriticalSection(&(screen->lock));
}
fps = encoder->fps;

View File

@ -26,6 +26,8 @@
rdpShadowScreen* shadow_screen_new(rdpShadowServer* server)
{
int x, y;
int width, height;
MONITOR_DEF* primary;
rdpShadowScreen* screen;
rdpShadowSubsystem* subsystem;
@ -38,11 +40,22 @@ rdpShadowScreen* shadow_screen_new(rdpShadowServer* server)
screen->server = server;
subsystem = server->subsystem;
primary = &(subsystem->monitors[0]);
screen->width = primary->right;
screen->height = primary->bottom;
if (!InitializeCriticalSectionAndSpinCount(&(screen->lock), 4000))
return NULL;
screen->primary = shadow_surface_new(server, screen->width, screen->height);
region16_init(&(screen->invalidRegion));
primary = &(subsystem->monitors[0]);
x = primary->left;
y = primary->top;
width = primary->right - primary->left;
height = primary->bottom - primary->top;
screen->width = width;
screen->height = height;
screen->primary = shadow_surface_new(server, x, y, width, height);
if (!screen->primary)
return NULL;
@ -57,6 +70,10 @@ void shadow_screen_free(rdpShadowScreen* screen)
if (!screen)
return;
DeleteCriticalSection(&(screen->lock));
region16_uninit(&(screen->invalidRegion));
if (screen->primary)
{
shadow_surface_free(screen->primary);

View File

@ -21,6 +21,9 @@
#include <freerdp/server/shadow.h>
#include <winpr/crt.h>
#include <winpr/synch.h>
struct rdp_shadow_screen
{
rdpShadowServer* server;
@ -28,6 +31,9 @@ struct rdp_shadow_screen
int width;
int height;
CRITICAL_SECTION lock;
REGION16 invalidRegion;
rdpShadowSurface* primary;
};

View File

@ -24,7 +24,7 @@
#include "shadow_surface.h"
rdpShadowSurface* shadow_surface_new(rdpShadowServer* server, int width, int height)
rdpShadowSurface* shadow_surface_new(rdpShadowServer* server, int x, int y, int width, int height)
{
rdpShadowSurface* surface;
@ -35,6 +35,8 @@ rdpShadowSurface* shadow_surface_new(rdpShadowServer* server, int width, int hei
surface->server = server;
surface->x = x;
surface->y = y;
surface->width = width;
surface->height = height;
surface->scanline = (surface->width + (surface->width % 4)) * 4;

View File

@ -28,6 +28,8 @@ struct rdp_shadow_surface
{
rdpShadowServer* server;
int x;
int y;
int width;
int height;
int scanline;
@ -41,7 +43,7 @@ struct rdp_shadow_surface
extern "C" {
#endif
rdpShadowSurface* shadow_surface_new(rdpShadowServer* server, int width, int height);
rdpShadowSurface* shadow_surface_new(rdpShadowServer* server, int x, int y, int width, int height);
void shadow_surface_free(rdpShadowSurface* surface);
#ifdef __cplusplus