diff --git a/server/.gitignore b/server/.gitignore index dac0f57c4..ef902f781 100644 --- a/server/.gitignore +++ b/server/.gitignore @@ -4,5 +4,5 @@ !/Sample !/Windows !/X11 -!/Shadow +!/shadow !/CmakeLists.txt diff --git a/server/shadow/.gitignore b/server/shadow/.gitignore new file mode 100644 index 000000000..662d7d541 --- /dev/null +++ b/server/shadow/.gitignore @@ -0,0 +1,2 @@ +freerdp-shadow + diff --git a/server/shadow/CMakeLists.txt b/server/shadow/CMakeLists.txt new file mode 100644 index 000000000..2fd6220d6 --- /dev/null +++ b/server/shadow/CMakeLists.txt @@ -0,0 +1,154 @@ +# FreeRDP: A Remote Desktop Protocol Implementation +# FreeRDP Shadow Server cmake build script +# +# Copyright 2014 Marc-Andre Moreau +# +# 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") +set(MODULE_PREFIX "FREERDP_SERVER_SHADOW") + +if(WITH_X11) + set(XEXT_FEATURE_TYPE "RECOMMENDED") + set(XEXT_FEATURE_PURPOSE "X11 extension") + set(XEXT_FEATURE_DESCRIPTION "X11 core extensions") + + set(XSHM_FEATURE_TYPE "RECOMMENDED") + set(XSHM_FEATURE_PURPOSE "X11 shared memory") + set(XSHM_FEATURE_DESCRIPTION "X11 shared memory extension") + + set(XINERAMA_FEATURE_TYPE "RECOMMENDED") + set(XINERAMA_FEATURE_PURPOSE "multi-monitor") + set(XINERAMA_FEATURE_DESCRIPTION "X11 multi-monitor extension") + + set(XTEST_FEATURE_TYPE "RECOMMENDED") + set(XTEST_FEATURE_PURPOSE "X11 input event injection") + set(XTEST_FEATURE_DESCRIPTION "X11 input event injection extension") + + set(XCURSOR_FEATURE_TYPE "RECOMMENDED") + set(XCURSOR_FEATURE_PURPOSE "cursor") + set(XCURSOR_FEATURE_DESCRIPTION "X11 cursor extension") + + set(XFIXES_FEATURE_TYPE "RECOMMENDED") + set(XFIXES_FEATURE_PURPOSE "X11 region") + set(XFIXES_FEATURE_DESCRIPTION "X11 region fix extension") + + set(XRANDR_FEATURE_TYPE "RECOMMENDED") + set(XRANDR_FEATURE_PURPOSE "X11 resize, rotate and reflect") + set(XRANDR_FEATURE_DESCRIPTION "X11 resize, rotate and reflect extension") + + set(XDAMAGE_FEATURE_TYPE "RECOMMENDED") + set(XDAMAGE_FEATURE_PURPOSE "X11 region damage") + set(XDAMAGE_FEATURE_DESCRIPTION "X11 region damage extension") + + find_feature(Xext ${XEXT_FEATURE_TYPE} ${XEXT_FEATURE_PURPOSE} ${XEXT_FEATURE_DESCRIPTION}) + find_feature(XShm ${XSHM_FEATURE_TYPE} ${XSHM_FEATURE_PURPOSE} ${XSHM_FEATURE_DESCRIPTION}) + find_feature(XTest ${XTEST_FEATURE_TYPE} ${XTEST_FEATURE_PURPOSE} ${XTEST_FEATURE_DESCRIPTION}) + find_feature(Xfixes ${XFIXES_FEATURE_TYPE} ${XFIXES_FEATURE_PURPOSE} ${XFIXES_FEATURE_DESCRIPTION}) + find_feature(XRandR ${XRANDR_FEATURE_TYPE} ${XRANDR_FEATURE_PURPOSE} ${XRANDR_FEATURE_DESCRIPTION}) + find_feature(Xdamage ${XDAMAGE_FEATURE_TYPE} ${XDAMAGE_FEATURE_PURPOSE} ${XDAMAGE_FEATURE_DESCRIPTION}) + find_feature(Xcursor ${XCURSOR_FEATURE_TYPE} ${XCURSOR_FEATURE_PURPOSE} ${XCURSOR_FEATURE_DESCRIPTION}) + find_feature(Xinerama ${XINERAMA_FEATURE_TYPE} ${XINERAMA_FEATURE_PURPOSE} ${XINERAMA_FEATURE_DESCRIPTION}) + + if(WITH_X11) + add_definitions(-DWITH_X11) + include_directories(${X11_INCLUDE_DIRS}) + list(APPEND ${MODULE_PREFIX}_X11_LIBS ${X11_LIBRARIES}) + endif() + + if(WITH_XSHM) + add_definitions(-DWITH_XSHM) + include_directories(${XSHM_INCLUDE_DIRS}) + list(APPEND ${MODULE_PREFIX}_X11_LIBS ${XSHM_LIBRARIES}) + endif() + + if(WITH_XEXT) + add_definitions(-DWITH_XEXT) + include_directories(${XEXT_INCLUDE_DIRS}) + list(APPEND ${MODULE_PREFIX}_X11_LIBS ${XEXT_LIBRARIES}) + endif() + + if(WITH_XINERAMA) + add_definitions(-DWITH_XINERAMA) + include_directories(${XINERAMA_INCLUDE_DIRS}) + list(APPEND ${MODULE_PREFIX}_X11_LIBS ${XINERAMA_LIBRARIES}) + endif() + + if(WITH_XCURSOR) + add_definitions(-DWITH_XCURSOR) + include_directories(${XCURSOR_INCLUDE_DIRS}) + list(APPEND ${MODULE_PREFIX}_X11_LIBS ${XCURSOR_LIBRARIES}) + endif() + + if(WITH_XDAMAGE) + add_definitions(-DWITH_XDAMAGE) + include_directories(${XDAMAGE_INCLUDE_DIRS}) + list(APPEND ${MODULE_PREFIX}_X11_LIBS ${XDAMAGE_LIBRARIES}) + endif() + + if(WITH_XFIXES) + add_definitions(-DWITH_XFIXES) + include_directories(${XFIXES_INCLUDE_DIRS}) + list(APPEND ${MODULE_PREFIX}_X11_LIBS ${XFIXES_LIBRARIES}) + endif() + + if(WITH_XTEST) + add_definitions(-DWITH_XTEST) + include_directories(${XTEST_INCLUDE_DIRS}) + list(APPEND ${MODULE_PREFIX}_X11_LIBS ${XTEST_LIBRARIES}) + endif() + + if(WITH_XRANDR) + add_definitions(-DWITH_XRANDR) + include_directories(${XRANDR_INCLUDE_DIRS}) + list(APPEND ${MODULE_PREFIX}_X11_LIBS ${XRANDR_LIBRARIES}) + endif() +endif() + +set(${MODULE_PREFIX}_SRCS + shadow.c + shadow.h) + +set(${MODULE_PREFIX}_X11_SRCS + X11/x11_input.c + X11/x11_peer.c + X11/x11_update.c + X11/x11_shadow.c + X11/x11_shadow.h) + +if(X11_FOUND) + list(APPEND ${MODULE_PREFIX}_SRCS ${${MODULE_PREFIX}_X11_SRCS}) + list(APPEND ${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_X11_LIBS}) +endif() + +add_executable(${MODULE_NAME} ${${MODULE_PREFIX}_SRCS}) +set_target_properties(${MODULE_NAME} PROPERTIES OUTPUT_NAME "freerdp-shadow") + +set_complex_link_libraries(VARIABLE ${MODULE_PREFIX}_LIBS + MONOLITHIC ${MONOLITHIC_BUILD} + MODULE freerdp + MODULES freerdp-core freerdp-common freerdp-codec freerdp-primitives freerdp-utils freerdp-gdi freerdp-crypto freerdp-locale) + +set_complex_link_libraries(VARIABLE ${MODULE_PREFIX}_LIBS + MONOLITHIC ${MONOLITHIC_BUILD} + MODULE winpr + MODULES winpr-sspi winpr-crt winpr-utils winpr-input winpr-sysinfo) + +set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} winpr-makecert-tool) + +target_link_libraries(${MODULE_NAME} ${${MODULE_PREFIX}_LIBS}) + +install(TARGETS ${MODULE_NAME} DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT server) + +set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Server/shadow") + diff --git a/server/shadow/X11/x11_input.c b/server/shadow/X11/x11_input.c new file mode 100644 index 000000000..314886b21 --- /dev/null +++ b/server/shadow/X11/x11_input.c @@ -0,0 +1,173 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * X11 Server Input + * + * Copyright 2011 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include + +#include +#include + +#include "x11_shadow.h" + +int xf_cursor_init(xfInfo* xfi) +{ +#ifdef WITH_XFIXES + int event; + int error; + + if (!XFixesQueryExtension(xfi->display, &event, &error)) + { + fprintf(stderr, "XFixesQueryExtension failed\n"); + return -1; + } + + xfi->xfixes_notify_event = event + XFixesCursorNotify; + + XFixesSelectCursorInput(xfi->display, DefaultRootWindow(xfi->display), XFixesDisplayCursorNotifyMask); +#endif + + return 0; +} + +void xf_input_synchronize_event(rdpInput* input, UINT32 flags) +{ + fprintf(stderr, "Client sent a synchronize event (flags:0x%X)\n", flags); +} + +void xf_input_keyboard_event(rdpInput* input, UINT16 flags, UINT16 code) +{ +#ifdef WITH_XTEST + DWORD vkcode; + DWORD keycode; + BOOL extended = FALSE; + xfPeerContext* xfp = (xfPeerContext*) input->context; + xfInfo* xfi = xfp->info; + + if (flags & KBD_FLAGS_EXTENDED) + extended = TRUE; + + if (extended) + code |= KBDEXT; + + vkcode = GetVirtualKeyCodeFromVirtualScanCode(code, 4); + keycode = GetKeycodeFromVirtualKeyCode(vkcode, KEYCODE_TYPE_EVDEV); + + if (keycode != 0) + { + XTestGrabControl(xfi->display, True); + + if (flags & KBD_FLAGS_DOWN) + XTestFakeKeyEvent(xfi->display, keycode, True, 0); + else if (flags & KBD_FLAGS_RELEASE) + XTestFakeKeyEvent(xfi->display, keycode, False, 0); + + XTestGrabControl(xfi->display, False); + } +#endif +} + +void xf_input_unicode_keyboard_event(rdpInput* input, UINT16 flags, UINT16 code) +{ + fprintf(stderr, "Client sent a unicode keyboard event (flags:0x%X code:0x%X)\n", flags, code); +} + +void xf_input_mouse_event(rdpInput* input, UINT16 flags, UINT16 x, UINT16 y) +{ +#ifdef WITH_XTEST + xfPeerContext* xfp = (xfPeerContext*) input->context; + int button = 0; + BOOL down = FALSE; + xfInfo* xfi = xfp->info; + + XTestGrabControl(xfi->display, True); + + if (flags & PTR_FLAGS_WHEEL) + { + BOOL negative = FALSE; + + if (flags & PTR_FLAGS_WHEEL_NEGATIVE) + negative = TRUE; + + button = (negative) ? 5 : 4; + + XTestFakeButtonEvent(xfi->display, button, True, 0); + XTestFakeButtonEvent(xfi->display, button, False, 0); + } + else + { + if (flags & PTR_FLAGS_MOVE) + XTestFakeMotionEvent(xfi->display, 0, x, y, 0); + + if (flags & PTR_FLAGS_BUTTON1) + button = 1; + else if (flags & PTR_FLAGS_BUTTON2) + button = 3; + else if (flags & PTR_FLAGS_BUTTON3) + button = 2; + + if (flags & PTR_FLAGS_DOWN) + down = TRUE; + + if (button != 0) + XTestFakeButtonEvent(xfi->display, button, down, 0); + } + + XTestGrabControl(xfi->display, False); +#endif +} + +void xf_input_extended_mouse_event(rdpInput* input, UINT16 flags, UINT16 x, UINT16 y) +{ +#ifdef WITH_XTEST + xfPeerContext* xfp = (xfPeerContext*) input->context; + int button = 0; + BOOL down = FALSE; + xfInfo* xfi = xfp->info; + + XTestGrabControl(xfi->display, True); + XTestFakeMotionEvent(xfi->display, 0, x, y, CurrentTime); + + if (flags & PTR_XFLAGS_BUTTON1) + button = 8; + else if (flags & PTR_XFLAGS_BUTTON2) + button = 9; + + if (flags & PTR_XFLAGS_DOWN) + down = TRUE; + + if (button != 0) + XTestFakeButtonEvent(xfi->display, button, down, 0); + + XTestGrabControl(xfi->display, False); +#endif +} + +void xf_input_register_callbacks(rdpInput* input) +{ + input->SynchronizeEvent = xf_input_synchronize_event; + input->KeyboardEvent = xf_input_keyboard_event; + input->UnicodeKeyboardEvent = xf_input_unicode_keyboard_event; + input->MouseEvent = xf_input_mouse_event; + input->ExtendedMouseEvent = xf_input_extended_mouse_event; +} diff --git a/server/shadow/X11/x11_peer.c b/server/shadow/X11/x11_peer.c new file mode 100644 index 000000000..ef161a5ba --- /dev/null +++ b/server/shadow/X11/x11_peer.c @@ -0,0 +1,612 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * + * Copyright 2011-2014 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include "x11_shadow.h" + +#ifdef WITH_XDAMAGE + +void xf_xdamage_init(xfInfo* xfi) +{ + int damage_event; + int damage_error; + int major, minor; + XGCValues values; + + if (XDamageQueryExtension(xfi->display, &damage_event, &damage_error) == 0) + { + fprintf(stderr, "XDamageQueryExtension failed\n"); + return; + } + + XDamageQueryVersion(xfi->display, &major, &minor); + + if (XDamageQueryVersion(xfi->display, &major, &minor) == 0) + { + fprintf(stderr, "XDamageQueryVersion failed\n"); + return; + } + else if (major < 1) + { + fprintf(stderr, "XDamageQueryVersion failed: major:%d minor:%d\n", major, minor); + return; + } + + xfi->xdamage_notify_event = damage_event + XDamageNotify; + xfi->xdamage = XDamageCreate(xfi->display, xfi->root_window, XDamageReportDeltaRectangles); + + if (xfi->xdamage == None) + { + fprintf(stderr, "XDamageCreate failed\n"); + return; + } + +#ifdef WITH_XFIXES + xfi->xdamage_region = XFixesCreateRegion(xfi->display, NULL, 0); + + if (xfi->xdamage_region == None) + { + fprintf(stderr, "XFixesCreateRegion failed\n"); + XDamageDestroy(xfi->display, xfi->xdamage); + xfi->xdamage = None; + return; + } +#endif + + values.subwindow_mode = IncludeInferiors; + xfi->xdamage_gc = XCreateGC(xfi->display, xfi->root_window, GCSubwindowMode, &values); + XSetFunction(xfi->display, xfi->xdamage_gc, GXcopy); +} + +#endif + +int xf_xshm_init(xfInfo* xfi) +{ + Bool pixmaps; + int major, minor; + + if (XShmQueryExtension(xfi->display) != False) + { + XShmQueryVersion(xfi->display, &major, &minor, &pixmaps); + + if (pixmaps != True) + { + fprintf(stderr, "XShmQueryVersion failed\n"); + return -1; + } + } + else + { + fprintf(stderr, "XShmQueryExtension failed\n"); + return -1; + } + + xfi->fb_shm_info.shmid = -1; + xfi->fb_shm_info.shmaddr = (char*) -1; + + xfi->fb_image = XShmCreateImage(xfi->display, xfi->visual, xfi->depth, + ZPixmap, NULL, &(xfi->fb_shm_info), xfi->width, xfi->height); + + if (!xfi->fb_image) + { + fprintf(stderr, "XShmCreateImage failed\n"); + return -1; + } + + xfi->fb_shm_info.shmid = shmget(IPC_PRIVATE, + xfi->fb_image->bytes_per_line * xfi->fb_image->height, IPC_CREAT | 0600); + + if (xfi->fb_shm_info.shmid == -1) + { + fprintf(stderr, "shmget failed\n"); + return -1; + } + + xfi->fb_shm_info.readOnly = False; + xfi->fb_shm_info.shmaddr = shmat(xfi->fb_shm_info.shmid, 0, 0); + xfi->fb_image->data = xfi->fb_shm_info.shmaddr; + + if (xfi->fb_shm_info.shmaddr == ((char*) -1)) + { + fprintf(stderr, "shmat failed\n"); + return -1; + } + + XShmAttach(xfi->display, &(xfi->fb_shm_info)); + XSync(xfi->display, False); + + shmctl(xfi->fb_shm_info.shmid, IPC_RMID, 0); + + fprintf(stderr, "display: %p root_window: %p width: %d height: %d depth: %d\n", + xfi->display, (void*) xfi->root_window, xfi->fb_image->width, xfi->fb_image->height, xfi->fb_image->depth); + + xfi->fb_pixmap = XShmCreatePixmap(xfi->display, + xfi->root_window, xfi->fb_image->data, &(xfi->fb_shm_info), + xfi->fb_image->width, xfi->fb_image->height, xfi->fb_image->depth); + + return 0; +} + +void xf_info_free(xfInfo *info) +{ + if (info->display) + XCloseDisplay(info->display); + + freerdp_clrconv_free(info->clrconv); + free(info); +} + +xfInfo* xf_info_init() +{ + int i; + xfInfo* xfi; + int pf_count; + int vi_count; + XVisualInfo* vi; + XVisualInfo* vis; + XVisualInfo template; + XPixmapFormatValues* pf; + XPixmapFormatValues* pfs; + + xfi = (xfInfo*) calloc(1, sizeof(xfInfo)); + + /** + * Recent X11 servers drop support for shared pixmaps + * To see if your X11 server supports shared pixmaps, use: + * xdpyinfo -ext MIT-SHM | grep "shared pixmaps" + */ + xfi->use_xshm = TRUE; + + setenv("DISPLAY", ":0", 1); /* Set DISPLAY variable if not already set */ + + if (!XInitThreads()) + fprintf(stderr, "warning: XInitThreads() failure\n"); + + xfi->display = XOpenDisplay(NULL); + + if (!xfi->display) + { + fprintf(stderr, "failed to open display: %s\n", XDisplayName(NULL)); + exit(1); + } + + xfi->xfds = ConnectionNumber(xfi->display); + xfi->number = DefaultScreen(xfi->display); + xfi->screen = ScreenOfDisplay(xfi->display, xfi->number); + xfi->depth = DefaultDepthOfScreen(xfi->screen); + xfi->width = WidthOfScreen(xfi->screen); + xfi->height = HeightOfScreen(xfi->screen); + xfi->root_window = DefaultRootWindow(xfi->display); + + pfs = XListPixmapFormats(xfi->display, &pf_count); + + if (!pfs) + { + fprintf(stderr, "XListPixmapFormats failed\n"); + exit(1); + } + + for (i = 0; i < pf_count; i++) + { + pf = pfs + i; + + if (pf->depth == xfi->depth) + { + xfi->bpp = pf->bits_per_pixel; + xfi->scanline_pad = pf->scanline_pad; + break; + } + } + XFree(pfs); + + ZeroMemory(&template, sizeof(template)); + template.class = TrueColor; + template.screen = xfi->number; + + vis = XGetVisualInfo(xfi->display, VisualClassMask | VisualScreenMask, &template, &vi_count); + + if (!vis) + { + fprintf(stderr, "XGetVisualInfo failed\n"); + exit(1); + } + + for (i = 0; i < vi_count; i++) + { + vi = vis + i; + + if (vi->depth == xfi->depth) + { + xfi->visual = vi->visual; + break; + } + } + XFree(vis); + + xfi->clrconv = freerdp_clrconv_new(CLRCONV_ALPHA | CLRCONV_INVERT); + + XSelectInput(xfi->display, xfi->root_window, SubstructureNotifyMask); + + if (xfi->use_xshm) + { + if (xf_xshm_init(xfi) < 0) + xfi->use_xshm = FALSE; + } + + if (xfi->use_xshm) + printf("Using X Shared Memory Extension (XShm)\n"); + +#ifdef WITH_XDAMAGE + xf_xdamage_init(xfi); +#endif + + xf_cursor_init(xfi); + + xfi->bytesPerPixel = 4; + xfi->activePeerCount = 0; + + freerdp_keyboard_init(0); + + return xfi; +} + +void xf_peer_context_new(freerdp_peer* client, xfPeerContext* context) +{ + context->info = xf_info_init(); + context->rfx_context = rfx_context_new(TRUE); + context->rfx_context->mode = RLGR3; + context->rfx_context->width = context->info->width; + context->rfx_context->height = context->info->height; + + rfx_context_set_pixel_format(context->rfx_context, RDP_PIXEL_FORMAT_B8G8R8A8); + + context->s = Stream_New(NULL, 65536); + Stream_Clear(context->s); + + context->updateReadyEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + context->updateSentEvent = CreateEvent(NULL, TRUE, FALSE, NULL); +} + +void xf_peer_context_free(freerdp_peer* client, xfPeerContext* context) +{ + if (context) + { + xf_info_free(context->info); + + CloseHandle(context->updateReadyEvent); + CloseHandle(context->updateSentEvent); + + Stream_Free(context->s, TRUE); + rfx_context_free(context->rfx_context); + } +} + +void xf_peer_init(freerdp_peer* client) +{ + xfInfo* xfi; + xfPeerContext* xfp; + + client->ContextSize = sizeof(xfPeerContext); + client->ContextNew = (psPeerContextNew) xf_peer_context_new; + client->ContextFree = (psPeerContextFree) xf_peer_context_free; + freerdp_peer_context_new(client); + + xfp = (xfPeerContext*) client->context; + + xfp->fps = 16; + xfi = xfp->info; + + xfp->mutex = CreateMutex(NULL, FALSE, NULL); +} + +void xf_peer_send_update(freerdp_peer* client) +{ + rdpUpdate* update; + SURFACE_BITS_COMMAND* cmd; + + update = client->update; + cmd = &update->surface_bits_command; + + if (cmd->bitmapDataLength) + update->SurfaceBits(update->context, cmd); +} + +BOOL xf_peer_get_fds(freerdp_peer* client, void** rfds, int* rcount) +{ + int fds; + HANDLE event; + xfPeerContext* xfp = (xfPeerContext*) client->context; + + event = xfp->updateReadyEvent; + fds = GetEventFileDescriptor(event); + rfds[*rcount] = (void*) (long) fds; + (*rcount)++; + + return TRUE; +} + +BOOL xf_peer_check_fds(freerdp_peer* client) +{ + xfInfo* xfi; + xfPeerContext* xfp; + + xfp = (xfPeerContext*) client->context; + xfi = xfp->info; + + if (WaitForSingleObject(xfp->updateReadyEvent, 0) == WAIT_OBJECT_0) + { + if (!xfp->activated) + return TRUE; + + xf_peer_send_update(client); + + ResetEvent(xfp->updateReadyEvent); + SetEvent(xfp->updateSentEvent); + } + + return TRUE; +} + +BOOL xf_peer_capabilities(freerdp_peer* client) +{ + return TRUE; +} + +BOOL xf_peer_post_connect(freerdp_peer* client) +{ + xfInfo* xfi; + xfPeerContext* xfp; + + xfp = (xfPeerContext*) client->context; + xfi = (xfInfo*) xfp->info; + + fprintf(stderr, "Client %s is activated", client->hostname); + if (client->settings->AutoLogonEnabled) + { + fprintf(stderr, " and wants to login automatically as %s\\%s", + client->settings->Domain ? client->settings->Domain : "", + client->settings->Username); + } + fprintf(stderr, "\n"); + + fprintf(stderr, "Client requested desktop: %dx%dx%d\n", + client->settings->DesktopWidth, client->settings->DesktopHeight, client->settings->ColorDepth); + + if (!client->settings->RemoteFxCodec) + { + fprintf(stderr, "Client does not support RemoteFX\n"); + return FALSE; + } + + /* A real server should tag the peer as activated here and start sending updates in main loop. */ + + client->settings->DesktopWidth = xfi->width; + client->settings->DesktopHeight = xfi->height; + + client->update->DesktopResize(client->update->context); + + /* Return FALSE here would stop the execution of the peer main loop. */ + return TRUE; +} + +BOOL xf_peer_activate(freerdp_peer* client) +{ + xfPeerContext* xfp = (xfPeerContext*) client->context; + + rfx_context_reset(xfp->rfx_context); + xfp->activated = TRUE; + + xfp->info->activePeerCount++; + + xfp->monitorThread = CreateThread(NULL, 0, + (LPTHREAD_START_ROUTINE) xf_update_thread, (void*) client, 0, NULL); + + return TRUE; +} + +const char* makecert_argv[4] = +{ + "makecert", + "-rdp", + "-live", + "-silent" +}; + +int makecert_argc = (sizeof(makecert_argv) / sizeof(char*)); + +int xf_generate_certificate(rdpSettings* settings) +{ + char* server_file_path; + MAKECERT_CONTEXT* context; + + server_file_path = GetCombinedPath(settings->ConfigPath, "server"); + + if (!PathFileExistsA(server_file_path)) + CreateDirectoryA(server_file_path, 0); + + settings->CertificateFile = GetCombinedPath(server_file_path, "server.crt"); + settings->PrivateKeyFile = GetCombinedPath(server_file_path, "server.key"); + + if ((!PathFileExistsA(settings->CertificateFile)) || + (!PathFileExistsA(settings->PrivateKeyFile))) + { + context = makecert_context_new(); + + makecert_context_process(context, makecert_argc, (char**) makecert_argv); + + makecert_context_set_output_file_name(context, "server"); + + if (!PathFileExistsA(settings->CertificateFile)) + makecert_context_output_certificate_file(context, server_file_path); + + if (!PathFileExistsA(settings->PrivateKeyFile)) + makecert_context_output_private_key_file(context, server_file_path); + + makecert_context_free(context); + } + + free(server_file_path); + + return 0; +} + +static void* xf_peer_main_loop(void* arg) +{ + int i; + int fds; + int max_fds; + int rcount; + void* rfds[32]; + fd_set rfds_set; + rdpSettings* settings; + xfPeerContext* xfp; + struct timeval timeout; + freerdp_peer* client = (freerdp_peer*) arg; + + ZeroMemory(rfds, sizeof(rfds)); + ZeroMemory(&timeout, sizeof(struct timeval)); + + fprintf(stderr, "We've got a client %s\n", client->hostname); + + xf_peer_init(client); + xfp = (xfPeerContext*) client->context; + settings = client->settings; + + xf_generate_certificate(settings); + + settings->RemoteFxCodec = TRUE; + settings->ColorDepth = 32; + + settings->NlaSecurity = FALSE; + settings->TlsSecurity = TRUE; + settings->RdpSecurity = FALSE; + + client->Capabilities = xf_peer_capabilities; + client->PostConnect = xf_peer_post_connect; + client->Activate = xf_peer_activate; + + xf_input_register_callbacks(client->input); + + client->Initialize(client); + + while (1) + { + rcount = 0; + + if (client->GetFileDescriptor(client, rfds, &rcount) != TRUE) + { + fprintf(stderr, "Failed to get FreeRDP file descriptor\n"); + break; + } + + if (xf_peer_get_fds(client, rfds, &rcount) != TRUE) + { + fprintf(stderr, "Failed to get xfreerdp file descriptor\n"); + break; + } + + max_fds = 0; + FD_ZERO(&rfds_set); + + for (i = 0; i < rcount; i++) + { + fds = (int)(long)(rfds[i]); + + if (fds > max_fds) + max_fds = fds; + + FD_SET(fds, &rfds_set); + } + + if (max_fds == 0) + break; + + timeout.tv_sec = 0; + timeout.tv_usec = 100; + + if (select(max_fds + 1, &rfds_set, NULL, NULL, &timeout) == -1) + { + /* these are not really errors */ + if (!((errno == EAGAIN) || + (errno == EWOULDBLOCK) || + (errno == EINPROGRESS) || + (errno == EINTR))) /* signal occurred */ + { + fprintf(stderr, "select failed\n"); + break; + } + } + + if (client->CheckFileDescriptor(client) != TRUE) + { + fprintf(stderr, "Failed to check freerdp file descriptor\n"); + break; + } + + if ((xf_peer_check_fds(client)) != TRUE) + { + fprintf(stderr, "Failed to check xfreerdp file descriptor\n"); + break; + } + } + + fprintf(stderr, "Client %s disconnected.\n", client->hostname); + + client->Disconnect(client); + + freerdp_peer_context_free(client); + freerdp_peer_free(client); + + ExitThread(0); + + return NULL; +} + +void xf_peer_accepted(freerdp_listener* instance, freerdp_peer* client) +{ + HANDLE thread; + + thread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) xf_peer_main_loop, client, 0, NULL); +} diff --git a/server/shadow/X11/x11_shadow.c b/server/shadow/X11/x11_shadow.c new file mode 100644 index 000000000..749b0759f --- /dev/null +++ b/server/shadow/X11/x11_shadow.c @@ -0,0 +1,153 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * + * Copyright 2011-2014 Marc-Andre Moreau + * + * 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 +#include +#include +#include + +#include +#include + +#include +#include + +#include "x11_shadow.h" + +void* x11_shadow_server_thread(void* param) +{ + int i; + int fds; + int max_fds; + int rcount; + void* rfds[32]; + fd_set rfds_set; + xfServer* server; + freerdp_listener* listener; + + server = (xfServer*) param; + listener = server->listener; + + while (1) + { + rcount = 0; + + ZeroMemory(rfds, sizeof(rfds)); + if (listener->GetFileDescriptor(listener, rfds, &rcount) != TRUE) + { + fprintf(stderr, "Failed to get FreeRDP file descriptor\n"); + break; + } + + max_fds = 0; + FD_ZERO(&rfds_set); + + for (i = 0; i < rcount; i++) + { + fds = (int)(long)(rfds[i]); + + if (fds > max_fds) + max_fds = fds; + + FD_SET(fds, &rfds_set); + } + + if (max_fds == 0) + break; + + if (select(max_fds + 1, &rfds_set, NULL, NULL, NULL) == -1) + { + /* these are not really errors */ + if (!((errno == EAGAIN) || + (errno == EWOULDBLOCK) || + (errno == EINPROGRESS) || + (errno == EINTR))) /* signal occurred */ + { + fprintf(stderr, "select failed\n"); + break; + } + } + + if (listener->CheckFileDescriptor(listener) != TRUE) + { + fprintf(stderr, "Failed to check FreeRDP file descriptor\n"); + break; + } + } + + ExitThread(0); + + return NULL; +} + +int x11_shadow_server_start(xfServer* server) +{ + server->thread = NULL; + + if (server->listener->Open(server->listener, NULL, 3389)) + { + server->thread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) + x11_shadow_server_thread, (void*) server, 0, NULL); + } + + return 0; +} + +int x11_shadow_server_stop(xfServer* server) +{ + if (server->thread) + { + TerminateThread(server->thread, 0); + WaitForSingleObject(server->thread, INFINITE); + CloseHandle(server->thread); + + server->listener->Close(server->listener); + } + return 0; +} + +HANDLE x11_shadow_server_get_thread(xfServer* server) +{ + return server->thread; +} + +xfServer* x11_shadow_server_new(int argc, char** argv) +{ + xfServer* server; + + server = (xfServer*) malloc(sizeof(xfServer)); + + if (server) + { + server->listener = freerdp_listener_new(); + server->listener->PeerAccepted = xf_peer_accepted; + } + + signal(SIGPIPE, SIG_IGN); + + return server; +} + +void x11_shadow_server_free(xfServer* server) +{ + if (server) + { + freerdp_listener_free(server->listener); + free(server); + } +} diff --git a/server/shadow/X11/x11_shadow.h b/server/shadow/X11/x11_shadow.h new file mode 100644 index 000000000..2f45ec7a4 --- /dev/null +++ b/server/shadow/X11/x11_shadow.h @@ -0,0 +1,173 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * + * Copyright 2011-2014 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FREERDP_SHADOW_SERVER_X11_H +#define FREERDP_SHADOW_SERVER_X11_H + +#include + +#include +#include + +typedef struct xf_info xfInfo; +typedef struct xf_server xfServer; + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Server Interface + */ + +FREERDP_API int x11_shadow_server_global_init(); +FREERDP_API int x11_shadow_server_global_uninit(); + +FREERDP_API int x11_shadow_server_start(xfServer* server); +FREERDP_API int x11_shadow_server_stop(xfServer* server); + +FREERDP_API HANDLE x11_shadow_server_get_thread(xfServer* server); + +FREERDP_API xfServer* x11_shadow_server_new(int argc, char** argv); +FREERDP_API void x11_shadow_server_free(xfServer* server); + +#ifdef __cplusplus +} +#endif + +#include +#include +#include +#include + +#include + +#ifdef WITH_XSHM +#include +#endif + +#ifdef WITH_XFIXES +#include +#endif + +#ifdef WITH_XTEST +#include +#endif + +#ifdef WITH_XDAMAGE +#include +#endif + +struct xf_info +{ + int bpp; + int xfds; + int depth; + int width; + int height; + int number; + XImage* image; + Screen* screen; + Visual* visual; + Display* display; + int scanline_pad; + int bytesPerPixel; + HCLRCONV clrconv; + BOOL use_xshm; + int activePeerCount; + + XImage* fb_image; + Pixmap fb_pixmap; + Window root_window; + XShmSegmentInfo fb_shm_info; + +#ifdef WITH_XDAMAGE + GC xdamage_gc; + Damage xdamage; + int xdamage_notify_event; + XserverRegion xdamage_region; +#endif + +#ifdef WITH_XFIXES + int xfixes_notify_event; +#endif +}; + +struct xf_server +{ + DWORD port; + HANDLE thread; + freerdp_listener* listener; +}; + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +typedef struct xf_peer_context xfPeerContext; + +#define PeerEvent_Base 0 + +#define PeerEvent_Class (PeerEvent_Base + 1) + +#define PeerEvent_EncodeRegion 1 + +struct xf_peer_context +{ + rdpContext _p; + + int fps; + wStream* s; + xfInfo* info; + HANDLE mutex; + BOOL activated; + HANDLE monitorThread; + HANDLE updateReadyEvent; + HANDLE updateSentEvent; + RFX_CONTEXT* rfx_context; +}; + +void xf_peer_accepted(freerdp_listener* instance, freerdp_peer* client); + +void* x11_shadow_server_thread(void* param); + +void* xf_update_thread(void* param); + +int xf_cursor_init(xfInfo* xfi); + +XImage* xf_snapshot(xfPeerContext* xfp, int x, int y, int width, int height); +void xf_xdamage_subtract_region(xfPeerContext* xfp, int x, int y, int width, int height); +int xf_update_encode(freerdp_peer* client, int x, int y, int width, int height); + +void xf_input_synchronize_event(rdpInput* input, UINT32 flags); +void xf_input_keyboard_event(rdpInput* input, UINT16 flags, UINT16 code); +void xf_input_unicode_keyboard_event(rdpInput* input, UINT16 flags, UINT16 code); +void xf_input_mouse_event(rdpInput* input, UINT16 flags, UINT16 x, UINT16 y); +void xf_input_extended_mouse_event(rdpInput* input, UINT16 flags, UINT16 x, UINT16 y); +void xf_input_register_callbacks(rdpInput* input); + +#endif /* FREERDP_SHADOW_SERVER_X11_H */ diff --git a/server/shadow/X11/x11_update.c b/server/shadow/X11/x11_update.c new file mode 100644 index 000000000..c6b499cdd --- /dev/null +++ b/server/shadow/X11/x11_update.c @@ -0,0 +1,214 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * + * Copyright 2011-2014 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include + +#include +#include + +#include "x11_shadow.h" + +XImage* xf_snapshot(xfPeerContext* xfp, int x, int y, int width, int height) +{ + XImage* image; + xfInfo* xfi = xfp->info; + + if (xfi->use_xshm) + { + XCopyArea(xfi->display, xfi->root_window, xfi->fb_pixmap, xfi->xdamage_gc, x, y, width, height, x, y); + image = xfi->fb_image; + } + else + { + image = XGetImage(xfi->display, xfi->root_window, x, y, width, height, AllPlanes, ZPixmap); + } + + return image; +} + +void xf_xdamage_subtract_region(xfPeerContext* xfp, int x, int y, int width, int height) +{ + XRectangle region; + xfInfo* xfi = xfp->info; + + region.x = x; + region.y = y; + region.width = width; + region.height = height; + +#ifdef WITH_XFIXES + XFixesSetRegion(xfi->display, xfi->xdamage_region, ®ion, 1); + XDamageSubtract(xfi->display, xfi->xdamage, xfi->xdamage_region, None); +#endif +} + +int xf_update_encode(freerdp_peer* client, int x, int y, int width, int height) +{ + wStream* s; + BYTE* data; + xfInfo* xfi; + RFX_RECT rect; + XImage* image; + rdpUpdate* update; + xfPeerContext* xfp; + SURFACE_BITS_COMMAND* cmd; + + update = client->update; + xfp = (xfPeerContext*) client->context; + cmd = &update->surface_bits_command; + xfi = xfp->info; + + if (width * height <= 0) + { + cmd->bitmapDataLength = 0; + return -1; + } + + s = xfp->s; + Stream_Clear(s); + Stream_SetPosition(s, 0); + + if (xfi->use_xshm) + { + /** + * Passing an offset source rectangle to rfx_compose_message() + * leads to protocol errors, so offset the data pointer instead. + */ + + rect.x = 0; + rect.y = 0; + rect.width = width; + rect.height = height; + + image = xf_snapshot(xfp, x, y, width, height); + + data = (BYTE*) image->data; + data = &data[(y * image->bytes_per_line) + (x * image->bits_per_pixel / 8)]; + + rfx_compose_message(xfp->rfx_context, s, &rect, 1, data, + width, height, image->bytes_per_line); + + cmd->destLeft = x; + cmd->destTop = y; + cmd->destRight = x + width; + cmd->destBottom = y + height; + } + else + { + rect.x = 0; + rect.y = 0; + rect.width = width; + rect.height = height; + + image = xf_snapshot(xfp, x, y, width, height); + + data = (BYTE*) image->data; + + rfx_compose_message(xfp->rfx_context, s, &rect, 1, data, + width, height, image->bytes_per_line); + + cmd->destLeft = x; + cmd->destTop = y; + cmd->destRight = x + width; + cmd->destBottom = y + height; + + XDestroyImage(image); + } + + cmd->bpp = 32; + cmd->codecID = client->settings->RemoteFxCodecId; + cmd->width = width; + cmd->height = height; + cmd->bitmapDataLength = Stream_GetPosition(s); + cmd->bitmapData = Stream_Buffer(s); + + return 0; +} + +void* xf_update_thread(void* param) +{ + xfInfo* xfi; + HANDLE event; + XEvent xevent; + DWORD beg, end; + DWORD diff, rate; + xfPeerContext* xfp; + freerdp_peer* client; + int x, y, width, height; + XDamageNotifyEvent* notify; + + client = (freerdp_peer*) param; + xfp = (xfPeerContext*) client->context; + xfi = xfp->info; + + rate = 1000 / xfp->fps; + + event = CreateFileDescriptorEvent(NULL, FALSE, FALSE, xfi->xfds); + + while (WaitForSingleObject(event, INFINITE) == WAIT_OBJECT_0) + { + beg = GetTickCount(); + + while (XPending(xfi->display) > 0) + { + ZeroMemory(&xevent, sizeof(xevent)); + XNextEvent(xfi->display, &xevent); + + if (xevent.type == xfi->xdamage_notify_event) + { + notify = (XDamageNotifyEvent*) &xevent; + + x = notify->area.x; + y = notify->area.y; + width = notify->area.width; + height = notify->area.height; + + if (xf_update_encode(client, x, y, width, height) >= 0) + { + xf_xdamage_subtract_region(xfp, x, y, width, height); + + SetEvent(xfp->updateReadyEvent); + + WaitForSingleObject(xfp->updateSentEvent, INFINITE); + ResetEvent(xfp->updateSentEvent); + } + } +#ifdef WITH_XFIXES + else if (xevent.type == xfi->xfixes_notify_event) + { + XFixesCursorImage* ci = XFixesGetCursorImage(xfi->display); + XFree(ci); + } +#endif + } + + end = GetTickCount(); + diff = end - beg; + + if (diff < rate) + Sleep(rate - diff); + } + + return NULL; +} diff --git a/server/shadow/shadow.c b/server/shadow/shadow.c new file mode 100644 index 000000000..cc1e9af4d --- /dev/null +++ b/server/shadow/shadow.c @@ -0,0 +1,113 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * FreeRDP Shadow Server + * + * Copyright 2014 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "shadow.h" + +#include "X11/x11_shadow.h" + +void* shadow_server_thread(void* param) +{ + ExitThread(0); + + return NULL; +} + +int shadow_server_start(xfServer* server) +{ + server->thread = NULL; + + if (server->listener->Open(server->listener, NULL, 3389)) + { + server->thread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) + shadow_server_thread, (void*) server, 0, NULL); + } + + return 0; +} + +int shadow_server_stop(xfServer* server) +{ + if (server->thread) + { + TerminateThread(server->thread, 0); + WaitForSingleObject(server->thread, INFINITE); + CloseHandle(server->thread); + + server->listener->Close(server->listener); + } + + return 0; +} + +HANDLE shadow_server_get_thread(xfServer* server) +{ + return server->thread; +} + +xfServer* shadow_server_new(int argc, char** argv) +{ + xfServer* server; + + server = (xfServer*) malloc(sizeof(xfServer)); + + if (server) + { + server->listener = freerdp_listener_new(); + server->listener->PeerAccepted = NULL; + } + + return server; +} + +void shadow_server_free(xfServer* server) +{ + if (server) + { + freerdp_listener_free(server->listener); + free(server); + } +} + +int main(int argc, char* argv[]) +{ + HANDLE thread; + xfServer* server; + DWORD dwExitCode; + + server = x11_shadow_server_new(argc, argv); + + if (!server) + return 0; + + x11_shadow_server_start(server); + + thread = x11_shadow_server_get_thread(server); + + WaitForSingleObject(thread, INFINITE); + + GetExitCodeThread(thread, &dwExitCode); + + x11_shadow_server_free(server); + + return 0; +} diff --git a/server/shadow/shadow.h b/server/shadow/shadow.h new file mode 100644 index 000000000..ef0f13638 --- /dev/null +++ b/server/shadow/shadow.h @@ -0,0 +1,41 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * FreeRDP Shadow Server + * + * Copyright 2014 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FREERDP_SHADOW_SERVER_H +#define FREERDP_SHADOW_SERVER_H + +#include + +#include +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + + + +#ifdef __cplusplus +} +#endif + +#endif /* FREERDP_SHADOW_SERVER_H */ +