summaryrefslogtreecommitdiffstats
path: root/server/shadow/X11
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 01:24:41 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 01:24:41 +0000
commita9bcc81f821d7c66f623779fa5147e728eb3c388 (patch)
tree98676963bcdd537ae5908a067a8eb110b93486a6 /server/shadow/X11
parentInitial commit. (diff)
downloadfreerdp3-a9bcc81f821d7c66f623779fa5147e728eb3c388.tar.xz
freerdp3-a9bcc81f821d7c66f623779fa5147e728eb3c388.zip
Adding upstream version 3.3.0+dfsg1.upstream/3.3.0+dfsg1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'server/shadow/X11')
-rw-r--r--server/shadow/X11/CMakeLists.txt72
-rw-r--r--server/shadow/X11/x11_shadow.c1513
-rw-r--r--server/shadow/X11/x11_shadow.h114
3 files changed, 1699 insertions, 0 deletions
diff --git a/server/shadow/X11/CMakeLists.txt b/server/shadow/X11/CMakeLists.txt
new file mode 100644
index 0000000..0d88a0e
--- /dev/null
+++ b/server/shadow/X11/CMakeLists.txt
@@ -0,0 +1,72 @@
+find_package(X11 REQUIRED)
+if(X11_FOUND)
+ add_definitions(-DWITH_X11)
+ include_directories(${X11_INCLUDE_DIR})
+ list(APPEND LIBS ${X11_LIBRARIES})
+endif()
+
+if(X11_XShm_FOUND)
+ add_definitions(-DWITH_XSHM)
+ include_directories(${X11_XShm_INCLUDE_PATH})
+ list(APPEND LIBS ${X11_Xext_LIB})
+endif()
+
+if(X11_Xext_FOUND)
+ add_definitions(-DWITH_XEXT)
+ list(APPEND LIBS ${X11_Xext_LIB})
+endif()
+
+if(X11_Xinerama_FOUND)
+ add_definitions(-DWITH_XINERAMA)
+ include_directories(${X11_Xinerama_INCLUDE_PATH})
+ list(APPEND LIBS ${X11_Xinerama_LIB})
+endif()
+
+if(X11_Xdamage_FOUND)
+ add_definitions(-DWITH_XDAMAGE)
+ include_directories(${X11_Xdamage_INCLUDE_PATH})
+ list(APPEND LIBS ${X11_Xdamage_LIB})
+endif()
+
+if(X11_Xfixes_FOUND)
+ add_definitions(-DWITH_XFIXES)
+ include_directories(${X11_Xfixes_INCLUDE_PATH})
+ list(APPEND LIBS ${X11_Xfixes_LIB})
+endif()
+
+if(X11_XTest_FOUND)
+ add_definitions(-DWITH_XTEST)
+ include_directories(${X11_XTest_INCLUDE_PATH})
+ list(APPEND LIBS ${X11_XTest_LIB})
+endif()
+
+# XCursor and XRandr are currently not used so don't link them
+#if(X11_Xcursor_FOUND)
+# add_definitions(-DWITH_XCURSOR)
+# include_directories(${X11_Xcursor_INCLUDE_PATH})
+# list(APPEND LIBS ${X11_Xcursor_LIB})
+#endif()
+
+#if(X11_Xrandr_FOUND)
+# add_definitions(-DWITH_XRANDR)
+# include_directories(${X11_Xrandr_INCLUDE_PATH})
+# list(APPEND LIBS ${X11_Xrandr_LIB})
+#endif()
+
+find_package(PAM)
+if(PAM_FOUND)
+ add_definitions(-DWITH_PAM)
+ include_directories(${PAM_INCLUDE_DIR})
+ list(APPEND LIBS ${PAM_LIBRARY})
+else()
+ message("building without PAM authentication support")
+endif()
+
+add_definitions(-DWITH_SHADOW_X11)
+add_library(freerdp-shadow-subsystem-impl STATIC
+ x11_shadow.h
+ x11_shadow.c
+)
+target_link_libraries(freerdp-shadow-subsystem-impl PRIVATE
+ ${LIBS}
+)
diff --git a/server/shadow/X11/x11_shadow.c b/server/shadow/X11/x11_shadow.c
new file mode 100644
index 0000000..5c1fab1
--- /dev/null
+++ b/server/shadow/X11/x11_shadow.c
@@ -0,0 +1,1513 @@
+/**
+ * FreeRDP: A Remote Desktop Protocol Implementation
+ *
+ * Copyright 2011-2014 Marc-Andre Moreau <marcandre.moreau@gmail.com>
+ * Copyright 2017 Armin Novak <armin.novak@thincast.com>
+ * Copyright 2017 Thincast Technologies GmbH
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <sys/ipc.h>
+#include <sys/shm.h>
+#include <sys/select.h>
+#include <sys/signal.h>
+
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+
+#include <winpr/crt.h>
+#include <winpr/assert.h>
+#include <winpr/path.h>
+#include <winpr/synch.h>
+#include <winpr/image.h>
+#include <winpr/sysinfo.h>
+
+#include <freerdp/log.h>
+#include <freerdp/codec/color.h>
+#include <freerdp/codec/region.h>
+
+#include "x11_shadow.h"
+
+#define TAG SERVER_TAG("shadow.x11")
+
+static UINT32 x11_shadow_enum_monitors(MONITOR_DEF* monitors, UINT32 maxMonitors);
+
+#ifdef WITH_PAM
+
+#include <security/pam_appl.h>
+
+typedef struct
+{
+ const char* user;
+ const char* domain;
+ const char* password;
+} SHADOW_PAM_AUTH_DATA;
+
+typedef struct
+{
+ char* service_name;
+ pam_handle_t* handle;
+ struct pam_conv pamc;
+ SHADOW_PAM_AUTH_DATA appdata;
+} SHADOW_PAM_AUTH_INFO;
+
+static int x11_shadow_pam_conv(int num_msg, const struct pam_message** msg,
+ struct pam_response** resp, void* appdata_ptr)
+{
+ int pam_status = PAM_CONV_ERR;
+ SHADOW_PAM_AUTH_DATA* appdata = NULL;
+ struct pam_response* response = NULL;
+ WINPR_ASSERT(num_msg >= 0);
+ appdata = (SHADOW_PAM_AUTH_DATA*)appdata_ptr;
+ WINPR_ASSERT(appdata);
+
+ if (!(response = (struct pam_response*)calloc((size_t)num_msg, sizeof(struct pam_response))))
+ return PAM_BUF_ERR;
+
+ for (int index = 0; index < num_msg; index++)
+ {
+ switch (msg[index]->msg_style)
+ {
+ case PAM_PROMPT_ECHO_ON:
+ response[index].resp = _strdup(appdata->user);
+
+ if (!response[index].resp)
+ goto out_fail;
+
+ response[index].resp_retcode = PAM_SUCCESS;
+ break;
+
+ case PAM_PROMPT_ECHO_OFF:
+ response[index].resp = _strdup(appdata->password);
+
+ if (!response[index].resp)
+ goto out_fail;
+
+ response[index].resp_retcode = PAM_SUCCESS;
+ break;
+
+ default:
+ pam_status = PAM_CONV_ERR;
+ goto out_fail;
+ }
+ }
+
+ *resp = response;
+ return PAM_SUCCESS;
+out_fail:
+
+ for (int index = 0; index < num_msg; ++index)
+ {
+ if (response[index].resp)
+ {
+ memset(response[index].resp, 0, strlen(response[index].resp));
+ free(response[index].resp);
+ }
+ }
+
+ memset(response, 0, sizeof(struct pam_response) * (size_t)num_msg);
+ free(response);
+ *resp = NULL;
+ return pam_status;
+}
+
+static BOOL x11_shadow_pam_get_service_name(SHADOW_PAM_AUTH_INFO* info)
+{
+ const char* base = "/etc/pam.d";
+ const char* hints[] = { "lightdm", "gdm", "xdm", "login", "sshd" };
+
+ for (size_t x = 0; x < ARRAYSIZE(hints); x++)
+ {
+ char path[MAX_PATH];
+ const char* hint = hints[x];
+
+ _snprintf(path, sizeof(path), "%s/%s", base, hint);
+ if (winpr_PathFileExists(path))
+ {
+
+ info->service_name = _strdup(hint);
+ return info->service_name != NULL;
+ }
+ }
+ WLog_WARN(TAG, "Could not determine PAM service name");
+ return FALSE;
+}
+
+static int x11_shadow_pam_authenticate(rdpShadowSubsystem* subsystem, rdpShadowClient* client,
+ const char* user, const char* domain, const char* password)
+{
+ int pam_status = 0;
+ SHADOW_PAM_AUTH_INFO info = { 0 };
+ WINPR_UNUSED(subsystem);
+ WINPR_UNUSED(client);
+
+ if (!x11_shadow_pam_get_service_name(&info))
+ return -1;
+
+ info.appdata.user = user;
+ info.appdata.domain = domain;
+ info.appdata.password = password;
+ info.pamc.conv = &x11_shadow_pam_conv;
+ info.pamc.appdata_ptr = &info.appdata;
+ pam_status = pam_start(info.service_name, 0, &info.pamc, &info.handle);
+
+ if (pam_status != PAM_SUCCESS)
+ {
+ WLog_ERR(TAG, "pam_start failure: %s", pam_strerror(info.handle, pam_status));
+ return -1;
+ }
+
+ pam_status = pam_authenticate(info.handle, 0);
+
+ if (pam_status != PAM_SUCCESS)
+ {
+ WLog_ERR(TAG, "pam_authenticate failure: %s", pam_strerror(info.handle, pam_status));
+ return -1;
+ }
+
+ pam_status = pam_acct_mgmt(info.handle, 0);
+
+ if (pam_status != PAM_SUCCESS)
+ {
+ WLog_ERR(TAG, "pam_acct_mgmt failure: %s", pam_strerror(info.handle, pam_status));
+ return -1;
+ }
+
+ return 1;
+}
+
+#endif
+
+static BOOL x11_shadow_input_synchronize_event(rdpShadowSubsystem* subsystem,
+ rdpShadowClient* client, UINT32 flags)
+{
+ /* TODO: Implement */
+ WLog_WARN(TAG, "not implemented");
+ return TRUE;
+}
+
+static BOOL x11_shadow_input_keyboard_event(rdpShadowSubsystem* subsystem, rdpShadowClient* client,
+ UINT16 flags, UINT8 code)
+{
+#ifdef WITH_XTEST
+ x11ShadowSubsystem* x11 = (x11ShadowSubsystem*)subsystem;
+ DWORD vkcode = 0;
+ DWORD keycode = 0;
+ DWORD scancode = 0;
+ BOOL extended = FALSE;
+
+ if (!client || !subsystem)
+ return FALSE;
+
+ if (flags & KBD_FLAGS_EXTENDED)
+ extended = TRUE;
+
+ scancode = code;
+ if (extended)
+ scancode |= KBDEXT;
+
+ vkcode = GetVirtualKeyCodeFromVirtualScanCode(scancode, WINPR_KBD_TYPE_IBM_ENHANCED);
+
+ if (extended)
+ vkcode |= KBDEXT;
+
+ keycode = GetKeycodeFromVirtualKeyCode(vkcode, WINPR_KEYCODE_TYPE_XKB);
+
+ if (keycode != 0)
+ {
+ XLockDisplay(x11->display);
+ XTestGrabControl(x11->display, True);
+
+ if (flags & KBD_FLAGS_RELEASE)
+ XTestFakeKeyEvent(x11->display, keycode, False, CurrentTime);
+ else
+ XTestFakeKeyEvent(x11->display, keycode, True, CurrentTime);
+
+ XTestGrabControl(x11->display, False);
+ XFlush(x11->display);
+ XUnlockDisplay(x11->display);
+ }
+
+#endif
+ return TRUE;
+}
+
+static BOOL x11_shadow_input_unicode_keyboard_event(rdpShadowSubsystem* subsystem,
+ rdpShadowClient* client, UINT16 flags,
+ UINT16 code)
+{
+ /* TODO: Implement */
+ WLog_WARN(TAG, "not implemented");
+ return TRUE;
+}
+
+static BOOL x11_shadow_input_mouse_event(rdpShadowSubsystem* subsystem, rdpShadowClient* client,
+ UINT16 flags, UINT16 x, UINT16 y)
+{
+#ifdef WITH_XTEST
+ x11ShadowSubsystem* x11 = (x11ShadowSubsystem*)subsystem;
+ unsigned int button = 0;
+ BOOL down = FALSE;
+ rdpShadowServer* server = NULL;
+ rdpShadowSurface* surface = NULL;
+
+ if (!subsystem || !client)
+ return FALSE;
+
+ server = subsystem->server;
+
+ if (!server)
+ return FALSE;
+
+ surface = server->surface;
+
+ if (!surface)
+ return FALSE;
+
+ x11->lastMouseClient = client;
+ x += surface->x;
+ y += surface->y;
+ XLockDisplay(x11->display);
+ XTestGrabControl(x11->display, True);
+
+ if (flags & PTR_FLAGS_WHEEL)
+ {
+ BOOL negative = FALSE;
+
+ if (flags & PTR_FLAGS_WHEEL_NEGATIVE)
+ negative = TRUE;
+
+ button = (negative) ? 5 : 4;
+ XTestFakeButtonEvent(x11->display, button, True, (unsigned long)CurrentTime);
+ XTestFakeButtonEvent(x11->display, button, False, (unsigned long)CurrentTime);
+ }
+ else if (flags & PTR_FLAGS_HWHEEL)
+ {
+ BOOL negative = FALSE;
+
+ if (flags & PTR_FLAGS_WHEEL_NEGATIVE)
+ negative = TRUE;
+
+ button = (negative) ? 7 : 6;
+ XTestFakeButtonEvent(x11->display, button, True, (unsigned long)CurrentTime);
+ XTestFakeButtonEvent(x11->display, button, False, (unsigned long)CurrentTime);
+ }
+ else
+ {
+ if (flags & PTR_FLAGS_MOVE)
+ XTestFakeMotionEvent(x11->display, 0, x, y, CurrentTime);
+
+ if (flags & PTR_FLAGS_BUTTON1)
+ button = 1;
+ else if (flags & PTR_FLAGS_BUTTON2)
+ button = 3;
+ else if (flags & PTR_FLAGS_BUTTON3)
+ button = 2;
+
+ if (flags & PTR_FLAGS_DOWN)
+ down = TRUE;
+
+ if (button)
+ XTestFakeButtonEvent(x11->display, button, down, CurrentTime);
+ }
+
+ XTestGrabControl(x11->display, False);
+ XFlush(x11->display);
+ XUnlockDisplay(x11->display);
+#endif
+ return TRUE;
+}
+
+static BOOL x11_shadow_input_extended_mouse_event(rdpShadowSubsystem* subsystem,
+ rdpShadowClient* client, UINT16 flags, UINT16 x,
+ UINT16 y)
+{
+#ifdef WITH_XTEST
+ x11ShadowSubsystem* x11 = (x11ShadowSubsystem*)subsystem;
+ UINT button = 0;
+ BOOL down = FALSE;
+ rdpShadowServer* server = NULL;
+ rdpShadowSurface* surface = NULL;
+
+ if (!subsystem || !client)
+ return FALSE;
+
+ server = subsystem->server;
+
+ if (!server)
+ return FALSE;
+
+ surface = server->surface;
+
+ if (!surface)
+ return FALSE;
+
+ x11->lastMouseClient = client;
+ x += surface->x;
+ y += surface->y;
+ XLockDisplay(x11->display);
+ XTestGrabControl(x11->display, True);
+ XTestFakeMotionEvent(x11->display, 0, x, y, CurrentTime);
+
+ if (flags & PTR_XFLAGS_BUTTON1)
+ button = 8;
+ else if (flags & PTR_XFLAGS_BUTTON2)
+ button = 9;
+
+ if (flags & PTR_XFLAGS_DOWN)
+ down = TRUE;
+
+ if (button)
+ XTestFakeButtonEvent(x11->display, button, down, CurrentTime);
+
+ XTestGrabControl(x11->display, False);
+ XFlush(x11->display);
+ XUnlockDisplay(x11->display);
+#endif
+ return TRUE;
+}
+
+static void x11_shadow_message_free(UINT32 id, SHADOW_MSG_OUT* msg)
+{
+ switch (id)
+ {
+ case SHADOW_MSG_OUT_POINTER_POSITION_UPDATE_ID:
+ free(msg);
+ break;
+
+ case SHADOW_MSG_OUT_POINTER_ALPHA_UPDATE_ID:
+ free(((SHADOW_MSG_OUT_POINTER_ALPHA_UPDATE*)msg)->xorMaskData);
+ free(((SHADOW_MSG_OUT_POINTER_ALPHA_UPDATE*)msg)->andMaskData);
+ free(msg);
+ break;
+
+ default:
+ WLog_ERR(TAG, "Unknown message id: %" PRIu32 "", id);
+ free(msg);
+ break;
+ }
+}
+
+static int x11_shadow_pointer_position_update(x11ShadowSubsystem* subsystem)
+{
+ UINT32 msgId = SHADOW_MSG_OUT_POINTER_POSITION_UPDATE_ID;
+ rdpShadowServer* server = NULL;
+ SHADOW_MSG_OUT_POINTER_POSITION_UPDATE templateMsg;
+ int count = 0;
+
+ if (!subsystem || !subsystem->common.server || !subsystem->common.server->clients)
+ return -1;
+
+ templateMsg.xPos = subsystem->common.pointerX;
+ templateMsg.yPos = subsystem->common.pointerY;
+ templateMsg.common.Free = x11_shadow_message_free;
+ server = subsystem->common.server;
+ ArrayList_Lock(server->clients);
+
+ for (size_t index = 0; index < ArrayList_Count(server->clients); index++)
+ {
+ SHADOW_MSG_OUT_POINTER_POSITION_UPDATE* msg = NULL;
+ rdpShadowClient* client = (rdpShadowClient*)ArrayList_GetItem(server->clients, index);
+
+ /* Skip the client which send us the latest mouse event */
+ if (client == subsystem->lastMouseClient)
+ continue;
+
+ msg = malloc(sizeof(templateMsg));
+
+ if (!msg)
+ {
+ count = -1;
+ break;
+ }
+
+ memcpy(msg, &templateMsg, sizeof(templateMsg));
+
+ if (shadow_client_post_msg(client, NULL, msgId, (SHADOW_MSG_OUT*)msg, NULL))
+ count++;
+ }
+
+ ArrayList_Unlock(server->clients);
+ return count;
+}
+
+static int x11_shadow_pointer_alpha_update(x11ShadowSubsystem* subsystem)
+{
+ SHADOW_MSG_OUT_POINTER_ALPHA_UPDATE* msg = NULL;
+ UINT32 msgId = SHADOW_MSG_OUT_POINTER_ALPHA_UPDATE_ID;
+ msg = (SHADOW_MSG_OUT_POINTER_ALPHA_UPDATE*)calloc(1,
+ sizeof(SHADOW_MSG_OUT_POINTER_ALPHA_UPDATE));
+
+ if (!msg)
+ return -1;
+
+ msg->xHot = subsystem->cursorHotX;
+ msg->yHot = subsystem->cursorHotY;
+ msg->width = subsystem->cursorWidth;
+ msg->height = subsystem->cursorHeight;
+
+ if (shadow_subsystem_pointer_convert_alpha_pointer_data(subsystem->cursorPixels, TRUE,
+ msg->width, msg->height, msg) < 0)
+ {
+ free(msg);
+ return -1;
+ }
+
+ msg->common.Free = x11_shadow_message_free;
+ return shadow_client_boardcast_msg(subsystem->common.server, NULL, msgId, (SHADOW_MSG_OUT*)msg,
+ NULL)
+ ? 1
+ : -1;
+}
+
+static int x11_shadow_query_cursor(x11ShadowSubsystem* subsystem, BOOL getImage)
+{
+ int x = 0;
+ int y = 0;
+ int n = 0;
+ rdpShadowServer* server = NULL;
+ rdpShadowSurface* surface = NULL;
+ server = subsystem->common.server;
+ surface = server->surface;
+
+ if (getImage)
+ {
+#ifdef WITH_XFIXES
+ UINT32* pDstPixel = NULL;
+ XFixesCursorImage* ci = NULL;
+ XLockDisplay(subsystem->display);
+ ci = XFixesGetCursorImage(subsystem->display);
+ XUnlockDisplay(subsystem->display);
+
+ if (!ci)
+ return -1;
+
+ x = ci->x;
+ y = ci->y;
+
+ if (ci->width > subsystem->cursorMaxWidth)
+ return -1;
+
+ if (ci->height > subsystem->cursorMaxHeight)
+ return -1;
+
+ subsystem->cursorHotX = ci->xhot;
+ subsystem->cursorHotY = ci->yhot;
+ subsystem->cursorWidth = ci->width;
+ subsystem->cursorHeight = ci->height;
+ subsystem->cursorId = ci->cursor_serial;
+ n = ci->width * ci->height;
+ pDstPixel = (UINT32*)subsystem->cursorPixels;
+
+ for (int k = 0; k < n; k++)
+ {
+ /* XFixesCursorImage.pixels is in *unsigned long*, which may be 8 bytes */
+ *pDstPixel++ = (UINT32)ci->pixels[k];
+ }
+
+ XFree(ci);
+ x11_shadow_pointer_alpha_update(subsystem);
+#endif
+ }
+ else
+ {
+ UINT32 mask = 0;
+ int win_x = 0;
+ int win_y = 0;
+ int root_x = 0;
+ int root_y = 0;
+ Window root = 0;
+ Window child = 0;
+ XLockDisplay(subsystem->display);
+
+ if (!XQueryPointer(subsystem->display, subsystem->root_window, &root, &child, &root_x,
+ &root_y, &win_x, &win_y, &mask))
+ {
+ XUnlockDisplay(subsystem->display);
+ return -1;
+ }
+
+ XUnlockDisplay(subsystem->display);
+ x = root_x;
+ y = root_y;
+ }
+
+ /* Convert to offset based on current surface */
+ if (surface)
+ {
+ x -= surface->x;
+ y -= surface->y;
+ }
+
+ if ((x != (INT64)subsystem->common.pointerX) || (y != (INT64)subsystem->common.pointerY))
+ {
+ subsystem->common.pointerX = x;
+ subsystem->common.pointerY = y;
+ x11_shadow_pointer_position_update(subsystem);
+ }
+
+ return 1;
+}
+
+static int x11_shadow_handle_xevent(x11ShadowSubsystem* subsystem, XEvent* xevent)
+{
+ if (xevent->type == MotionNotify)
+ {
+ }
+
+#ifdef WITH_XFIXES
+ else if (xevent->type == subsystem->xfixes_cursor_notify_event)
+ {
+ x11_shadow_query_cursor(subsystem, TRUE);
+ }
+
+#endif
+ else
+ {
+ }
+
+ return 1;
+}
+
+static void x11_shadow_validate_region(x11ShadowSubsystem* subsystem, int x, int y, int width,
+ int height)
+{
+ XRectangle region;
+
+ if (!subsystem->use_xfixes || !subsystem->use_xdamage)
+ return;
+
+ region.x = x;
+ region.y = y;
+ region.width = width;
+ region.height = height;
+#if defined(WITH_XFIXES) && defined(WITH_XDAMAGE)
+ XLockDisplay(subsystem->display);
+ XFixesSetRegion(subsystem->display, subsystem->xdamage_region, &region, 1);
+ XDamageSubtract(subsystem->display, subsystem->xdamage, subsystem->xdamage_region, None);
+ XUnlockDisplay(subsystem->display);
+#endif
+}
+
+static int x11_shadow_blend_cursor(x11ShadowSubsystem* subsystem)
+{
+ UINT32 nXSrc = 0;
+ UINT32 nYSrc = 0;
+ INT64 nXDst = 0;
+ INT64 nYDst = 0;
+ UINT32 nWidth = 0;
+ UINT32 nHeight = 0;
+ UINT32 nSrcStep = 0;
+ UINT32 nDstStep = 0;
+ BYTE* pSrcData = NULL;
+ BYTE* pDstData = NULL;
+ BYTE A = 0;
+ BYTE R = 0;
+ BYTE G = 0;
+ BYTE B = 0;
+ rdpShadowSurface* surface = NULL;
+
+ if (!subsystem)
+ return -1;
+
+ surface = subsystem->common.server->surface;
+ nXSrc = 0;
+ nYSrc = 0;
+ nWidth = subsystem->cursorWidth;
+ nHeight = subsystem->cursorHeight;
+ nXDst = subsystem->common.pointerX - subsystem->cursorHotX;
+ nYDst = subsystem->common.pointerY - subsystem->cursorHotY;
+
+ if (nXDst >= surface->width)
+ return 1;
+
+ if (nXDst < 0)
+ {
+ nXDst *= -1;
+
+ if (nXDst >= nWidth)
+ return 1;
+
+ nXSrc = (UINT32)nXDst;
+ nWidth -= nXDst;
+ nXDst = 0;
+ }
+
+ if (nYDst >= surface->height)
+ return 1;
+
+ if (nYDst < 0)
+ {
+ nYDst *= -1;
+
+ if (nYDst >= nHeight)
+ return 1;
+
+ nYSrc = (UINT32)nYDst;
+ nHeight -= nYDst;
+ nYDst = 0;
+ }
+
+ if ((nXDst + nWidth) > surface->width)
+ nWidth = surface->width - nXDst;
+
+ if ((nYDst + nHeight) > surface->height)
+ nHeight = surface->height - nYDst;
+
+ pSrcData = subsystem->cursorPixels;
+ nSrcStep = subsystem->cursorWidth * 4;
+ pDstData = surface->data;
+ nDstStep = surface->scanline;
+
+ for (int y = 0; y < nHeight; y++)
+ {
+ const BYTE* pSrcPixel = &pSrcData[((nYSrc + y) * nSrcStep) + (nXSrc * 4)];
+ BYTE* pDstPixel = &pDstData[((nYDst + y) * nDstStep) + (nXDst * 4)];
+
+ for (int x = 0; x < nWidth; x++)
+ {
+ B = *pSrcPixel++;
+ G = *pSrcPixel++;
+ R = *pSrcPixel++;
+ A = *pSrcPixel++;
+
+ if (A == 0xFF)
+ {
+ pDstPixel[0] = B;
+ pDstPixel[1] = G;
+ pDstPixel[2] = R;
+ }
+ else
+ {
+ pDstPixel[0] = B + (pDstPixel[0] * (0xFF - A) + (0xFF / 2)) / 0xFF;
+ pDstPixel[1] = G + (pDstPixel[1] * (0xFF - A) + (0xFF / 2)) / 0xFF;
+ pDstPixel[2] = R + (pDstPixel[2] * (0xFF - A) + (0xFF / 2)) / 0xFF;
+ }
+
+ pDstPixel[3] = 0xFF;
+ pDstPixel += 4;
+ }
+ }
+
+ return 1;
+}
+
+static BOOL x11_shadow_check_resize(x11ShadowSubsystem* subsystem)
+{
+ XWindowAttributes attr;
+ XLockDisplay(subsystem->display);
+ XGetWindowAttributes(subsystem->display, subsystem->root_window, &attr);
+ XUnlockDisplay(subsystem->display);
+
+ if (attr.width != (INT64)subsystem->width || attr.height != (INT64)subsystem->height)
+ {
+ MONITOR_DEF* virtualScreen = &(subsystem->common.virtualScreen);
+
+ /* Screen size changed. Refresh monitor definitions and trigger screen resize */
+ subsystem->common.numMonitors = x11_shadow_enum_monitors(subsystem->common.monitors, 16);
+ shadow_screen_resize(subsystem->common.server->screen);
+ subsystem->width = attr.width;
+ subsystem->height = attr.height;
+
+ virtualScreen->left = 0;
+ virtualScreen->top = 0;
+ virtualScreen->right = subsystem->width - 1;
+ virtualScreen->bottom = subsystem->height - 1;
+ virtualScreen->flags = 1;
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static int x11_shadow_error_handler_for_capture(Display* display, XErrorEvent* event)
+{
+ char msg[256];
+ XGetErrorText(display, event->error_code, (char*)&msg, sizeof(msg));
+ WLog_ERR(TAG, "X11 error: %s Error code: %x, request code: %x, minor code: %x", msg,
+ event->error_code, event->request_code, event->minor_code);
+
+ /* Ignore BAD MATCH error during image capture. Abort in other case */
+ if (event->error_code != BadMatch)
+ {
+ abort();
+ }
+
+ return 0;
+}
+
+static int x11_shadow_screen_grab(x11ShadowSubsystem* subsystem)
+{
+ int rc = 0;
+ size_t count = 0;
+ int status = -1;
+ int x = 0;
+ int y = 0;
+ int width = 0;
+ int height = 0;
+ XImage* image = NULL;
+ rdpShadowServer* server = NULL;
+ rdpShadowSurface* surface = NULL;
+ RECTANGLE_16 invalidRect;
+ RECTANGLE_16 surfaceRect;
+ const RECTANGLE_16* extents = NULL;
+ server = subsystem->common.server;
+ surface = server->surface;
+ count = ArrayList_Count(server->clients);
+
+ if (count < 1)
+ return 1;
+
+ EnterCriticalSection(&surface->lock);
+ surfaceRect.left = 0;
+ surfaceRect.top = 0;
+ surfaceRect.right = surface->width;
+ surfaceRect.bottom = surface->height;
+ LeaveCriticalSection(&surface->lock);
+
+ XLockDisplay(subsystem->display);
+ /*
+ * Ignore BadMatch error during image capture. The screen size may be
+ * changed outside. We will resize to correct resolution at next frame
+ */
+ XSetErrorHandler(x11_shadow_error_handler_for_capture);
+#if defined(WITH_XDAMAGE)
+ if (subsystem->use_xshm)
+ {
+ image = subsystem->fb_image;
+ XCopyArea(subsystem->display, subsystem->root_window, subsystem->fb_pixmap,
+ subsystem->xshm_gc, 0, 0, subsystem->width, subsystem->height, 0, 0);
+
+ EnterCriticalSection(&surface->lock);
+ status = shadow_capture_compare(surface->data, surface->scanline, surface->width,
+ surface->height, (BYTE*)&(image->data[surface->width * 4]),
+ image->bytes_per_line, &invalidRect);
+ LeaveCriticalSection(&surface->lock);
+ }
+ else
+#endif
+ {
+ EnterCriticalSection(&surface->lock);
+ image = XGetImage(subsystem->display, subsystem->root_window, surface->x, surface->y,
+ surface->width, surface->height, AllPlanes, ZPixmap);
+
+ if (image)
+ {
+ status = shadow_capture_compare(surface->data, surface->scanline, surface->width,
+ surface->height, (BYTE*)image->data,
+ image->bytes_per_line, &invalidRect);
+ }
+ LeaveCriticalSection(&surface->lock);
+ if (!image)
+ {
+ /*
+ * BadMatch error happened. The size may have been changed again.
+ * Give up this frame and we will resize again in next frame
+ */
+ goto fail_capture;
+ }
+ }
+
+ /* Restore the default error handler */
+ XSetErrorHandler(NULL);
+ XSync(subsystem->display, False);
+ XUnlockDisplay(subsystem->display);
+
+ if (status)
+ {
+ BOOL empty = 0;
+ EnterCriticalSection(&surface->lock);
+ region16_union_rect(&(surface->invalidRegion), &(surface->invalidRegion), &invalidRect);
+ region16_intersect_rect(&(surface->invalidRegion), &(surface->invalidRegion), &surfaceRect);
+ empty = region16_is_empty(&(surface->invalidRegion));
+ LeaveCriticalSection(&surface->lock);
+
+ if (!empty)
+ {
+ BOOL success = 0;
+ EnterCriticalSection(&surface->lock);
+ extents = region16_extents(&(surface->invalidRegion));
+ x = extents->left;
+ y = extents->top;
+ width = extents->right - extents->left;
+ height = extents->bottom - extents->top;
+ WINPR_ASSERT(image);
+ WINPR_ASSERT(image->bytes_per_line >= 0);
+ WINPR_ASSERT(width >= 0);
+ WINPR_ASSERT(height >= 0);
+ success = freerdp_image_copy(surface->data, surface->format, surface->scanline, x, y,
+ (UINT32)width, (UINT32)height, (BYTE*)image->data,
+ PIXEL_FORMAT_BGRX32, (UINT32)image->bytes_per_line, x, y,
+ NULL, FREERDP_FLIP_NONE);
+ LeaveCriticalSection(&surface->lock);
+ if (!success)
+ goto fail_capture;
+
+ // x11_shadow_blend_cursor(subsystem);
+ count = ArrayList_Count(server->clients);
+ shadow_subsystem_frame_update(&subsystem->common);
+
+ if (count == 1)
+ {
+ rdpShadowClient* client = NULL;
+ client = (rdpShadowClient*)ArrayList_GetItem(server->clients, 0);
+
+ if (client)
+ subsystem->common.captureFrameRate =
+ shadow_encoder_preferred_fps(client->encoder);
+ }
+
+ EnterCriticalSection(&surface->lock);
+ region16_clear(&(surface->invalidRegion));
+ LeaveCriticalSection(&surface->lock);
+ }
+ }
+
+ rc = 1;
+fail_capture:
+ if (!subsystem->use_xshm && image)
+ XDestroyImage(image);
+
+ if (rc != 1)
+ {
+ XSetErrorHandler(NULL);
+ XSync(subsystem->display, False);
+ XUnlockDisplay(subsystem->display);
+ }
+
+ return rc;
+}
+
+static int x11_shadow_subsystem_process_message(x11ShadowSubsystem* subsystem, wMessage* message)
+{
+ switch (message->id)
+ {
+ case SHADOW_MSG_IN_REFRESH_REQUEST_ID:
+ shadow_subsystem_frame_update((rdpShadowSubsystem*)subsystem);
+ break;
+
+ default:
+ WLog_ERR(TAG, "Unknown message id: %" PRIu32 "", message->id);
+ break;
+ }
+
+ if (message->Free)
+ message->Free(message);
+
+ return 1;
+}
+
+static DWORD WINAPI x11_shadow_subsystem_thread(LPVOID arg)
+{
+ x11ShadowSubsystem* subsystem = (x11ShadowSubsystem*)arg;
+ XEvent xevent;
+ DWORD status = 0;
+ DWORD nCount = 0;
+ UINT64 cTime = 0;
+ DWORD dwTimeout = 0;
+ DWORD dwInterval = 0;
+ UINT64 frameTime = 0;
+ HANDLE events[32];
+ wMessage message;
+ wMessagePipe* MsgPipe = NULL;
+ MsgPipe = subsystem->common.MsgPipe;
+ nCount = 0;
+ events[nCount++] = subsystem->common.event;
+ events[nCount++] = MessageQueue_Event(MsgPipe->In);
+ subsystem->common.captureFrameRate = 16;
+ dwInterval = 1000 / subsystem->common.captureFrameRate;
+ frameTime = GetTickCount64() + dwInterval;
+
+ while (1)
+ {
+ cTime = GetTickCount64();
+ dwTimeout = (cTime > frameTime) ? 0 : frameTime - cTime;
+ status = WaitForMultipleObjects(nCount, events, FALSE, dwTimeout);
+
+ if (WaitForSingleObject(MessageQueue_Event(MsgPipe->In), 0) == WAIT_OBJECT_0)
+ {
+ if (MessageQueue_Peek(MsgPipe->In, &message, TRUE))
+ {
+ if (message.id == WMQ_QUIT)
+ break;
+
+ x11_shadow_subsystem_process_message(subsystem, &message);
+ }
+ }
+
+ if (WaitForSingleObject(subsystem->common.event, 0) == WAIT_OBJECT_0)
+ {
+ XLockDisplay(subsystem->display);
+
+ if (XEventsQueued(subsystem->display, QueuedAlready))
+ {
+ XNextEvent(subsystem->display, &xevent);
+ x11_shadow_handle_xevent(subsystem, &xevent);
+ }
+
+ XUnlockDisplay(subsystem->display);
+ }
+
+ if ((status == WAIT_TIMEOUT) || (GetTickCount64() > frameTime))
+ {
+ x11_shadow_check_resize(subsystem);
+ x11_shadow_screen_grab(subsystem);
+ x11_shadow_query_cursor(subsystem, FALSE);
+ dwInterval = 1000 / subsystem->common.captureFrameRate;
+ frameTime += dwInterval;
+ }
+ }
+
+ ExitThread(0);
+ return 0;
+}
+
+static int x11_shadow_subsystem_base_init(x11ShadowSubsystem* subsystem)
+{
+ if (subsystem->display)
+ return 1; /* initialize once */
+
+ if (!getenv("DISPLAY"))
+ setenv("DISPLAY", ":0", 1);
+
+ if (!XInitThreads())
+ return -1;
+
+ subsystem->display = XOpenDisplay(NULL);
+
+ if (!subsystem->display)
+ {
+ WLog_ERR(TAG, "failed to open display: %s", XDisplayName(NULL));
+ return -1;
+ }
+
+ subsystem->xfds = ConnectionNumber(subsystem->display);
+ subsystem->number = DefaultScreen(subsystem->display);
+ subsystem->screen = ScreenOfDisplay(subsystem->display, subsystem->number);
+ subsystem->depth = DefaultDepthOfScreen(subsystem->screen);
+ subsystem->width = WidthOfScreen(subsystem->screen);
+ subsystem->height = HeightOfScreen(subsystem->screen);
+ subsystem->root_window = RootWindow(subsystem->display, subsystem->number);
+ return 1;
+}
+
+static int x11_shadow_xfixes_init(x11ShadowSubsystem* subsystem)
+{
+#ifdef WITH_XFIXES
+ int xfixes_event = 0;
+ int xfixes_error = 0;
+ int major = 0;
+ int minor = 0;
+
+ if (!XFixesQueryExtension(subsystem->display, &xfixes_event, &xfixes_error))
+ return -1;
+
+ if (!XFixesQueryVersion(subsystem->display, &major, &minor))
+ return -1;
+
+ subsystem->xfixes_cursor_notify_event = xfixes_event + XFixesCursorNotify;
+ XFixesSelectCursorInput(subsystem->display, subsystem->root_window,
+ XFixesDisplayCursorNotifyMask);
+ return 1;
+#else
+ return -1;
+#endif
+}
+
+static int x11_shadow_xinerama_init(x11ShadowSubsystem* subsystem)
+{
+#ifdef WITH_XINERAMA
+ int xinerama_event = 0;
+ int xinerama_error = 0;
+ x11_shadow_subsystem_base_init(subsystem);
+
+ if (!XineramaQueryExtension(subsystem->display, &xinerama_event, &xinerama_error))
+ return -1;
+
+#if defined(WITH_XDAMAGE)
+ int major = 0;
+ int minor = 0;
+ if (!XDamageQueryVersion(subsystem->display, &major, &minor))
+ return -1;
+#endif
+
+ if (!XineramaIsActive(subsystem->display))
+ return -1;
+
+ return 1;
+#else
+ return -1;
+#endif
+}
+
+static int x11_shadow_xdamage_init(x11ShadowSubsystem* subsystem)
+{
+#ifdef WITH_XDAMAGE
+ int major = 0;
+ int minor = 0;
+ int damage_event = 0;
+ int damage_error = 0;
+
+ if (!subsystem->use_xfixes)
+ return -1;
+
+ if (!XDamageQueryExtension(subsystem->display, &damage_event, &damage_error))
+ return -1;
+
+ if (!XDamageQueryVersion(subsystem->display, &major, &minor))
+ return -1;
+
+ if (major < 1)
+ return -1;
+
+ subsystem->xdamage_notify_event = damage_event + XDamageNotify;
+ subsystem->xdamage =
+ XDamageCreate(subsystem->display, subsystem->root_window, XDamageReportDeltaRectangles);
+
+ if (!subsystem->xdamage)
+ return -1;
+
+#ifdef WITH_XFIXES
+ subsystem->xdamage_region = XFixesCreateRegion(subsystem->display, NULL, 0);
+
+ if (!subsystem->xdamage_region)
+ return -1;
+
+#endif
+ return 1;
+#else
+ return -1;
+#endif
+}
+
+static int x11_shadow_xshm_init(x11ShadowSubsystem* subsystem)
+{
+ Bool pixmaps = 0;
+ int major = 0;
+ int minor = 0;
+ XGCValues values;
+
+ if (!XShmQueryExtension(subsystem->display))
+ return -1;
+
+ if (!XShmQueryVersion(subsystem->display, &major, &minor, &pixmaps))
+ return -1;
+
+ if (!pixmaps)
+ return -1;
+
+ subsystem->fb_shm_info.shmid = -1;
+ subsystem->fb_shm_info.shmaddr = (char*)-1;
+ subsystem->fb_shm_info.readOnly = False;
+ subsystem->fb_image =
+ XShmCreateImage(subsystem->display, subsystem->visual, subsystem->depth, ZPixmap, NULL,
+ &(subsystem->fb_shm_info), subsystem->width, subsystem->height);
+
+ if (!subsystem->fb_image)
+ {
+ WLog_ERR(TAG, "XShmCreateImage failed");
+ return -1;
+ }
+
+ subsystem->fb_shm_info.shmid = shmget(
+ IPC_PRIVATE, 1ull * subsystem->fb_image->bytes_per_line * subsystem->fb_image->height,
+ IPC_CREAT | 0600);
+
+ if (subsystem->fb_shm_info.shmid == -1)
+ {
+ WLog_ERR(TAG, "shmget failed");
+ return -1;
+ }
+
+ subsystem->fb_shm_info.shmaddr = shmat(subsystem->fb_shm_info.shmid, 0, 0);
+ subsystem->fb_image->data = subsystem->fb_shm_info.shmaddr;
+
+ if (subsystem->fb_shm_info.shmaddr == ((char*)-1))
+ {
+ WLog_ERR(TAG, "shmat failed");
+ return -1;
+ }
+
+ if (!XShmAttach(subsystem->display, &(subsystem->fb_shm_info)))
+ return -1;
+
+ XSync(subsystem->display, False);
+ shmctl(subsystem->fb_shm_info.shmid, IPC_RMID, 0);
+ subsystem->fb_pixmap =
+ XShmCreatePixmap(subsystem->display, subsystem->root_window, subsystem->fb_image->data,
+ &(subsystem->fb_shm_info), subsystem->fb_image->width,
+ subsystem->fb_image->height, subsystem->fb_image->depth);
+ XSync(subsystem->display, False);
+
+ if (!subsystem->fb_pixmap)
+ return -1;
+
+ values.subwindow_mode = IncludeInferiors;
+ values.graphics_exposures = False;
+#if defined(WITH_XDAMAGE)
+ subsystem->xshm_gc = XCreateGC(subsystem->display, subsystem->root_window,
+ GCSubwindowMode | GCGraphicsExposures, &values);
+ XSetFunction(subsystem->display, subsystem->xshm_gc, GXcopy);
+#endif
+ XSync(subsystem->display, False);
+ return 1;
+}
+
+UINT32 x11_shadow_enum_monitors(MONITOR_DEF* monitors, UINT32 maxMonitors)
+{
+ Display* display = NULL;
+ int displayWidth = 0;
+ int displayHeight = 0;
+ int numMonitors = 0;
+
+ if (!getenv("DISPLAY"))
+ setenv("DISPLAY", ":0", 1);
+
+ display = XOpenDisplay(NULL);
+
+ if (!display)
+ {
+ WLog_ERR(TAG, "failed to open display: %s", XDisplayName(NULL));
+ return -1;
+ }
+
+ displayWidth = WidthOfScreen(DefaultScreenOfDisplay(display));
+ displayHeight = HeightOfScreen(DefaultScreenOfDisplay(display));
+#ifdef WITH_XINERAMA
+ {
+#if defined(WITH_XDAMAGE)
+ int major = 0;
+ int minor = 0;
+#endif
+ int xinerama_event = 0;
+ int xinerama_error = 0;
+ XineramaScreenInfo* screens = NULL;
+
+ const Bool xinerama = XineramaQueryExtension(display, &xinerama_event, &xinerama_error);
+ const Bool damage =
+#if defined(WITH_XDAMAGE)
+ XDamageQueryVersion(display, &major, &minor);
+#else
+ False;
+#endif
+
+ if (xinerama && damage && XineramaIsActive(display))
+ {
+ screens = XineramaQueryScreens(display, &numMonitors);
+
+ if (numMonitors > (INT64)maxMonitors)
+ numMonitors = (int)maxMonitors;
+
+ if (screens && (numMonitors > 0))
+ {
+ for (int index = 0; index < numMonitors; index++)
+ {
+ MONITOR_DEF* monitor = &monitors[index];
+ const XineramaScreenInfo* screen = &screens[index];
+
+ monitor->left = screen->x_org;
+ monitor->top = screen->y_org;
+ monitor->right = monitor->left + screen->width - 1;
+ monitor->bottom = monitor->top + screen->height - 1;
+ monitor->flags = (index == 0) ? 1 : 0;
+ }
+ }
+
+ XFree(screens);
+ }
+ }
+#endif
+ XCloseDisplay(display);
+
+ if (numMonitors < 1)
+ {
+ MONITOR_DEF* monitor = &monitors[0];
+ numMonitors = 1;
+
+ monitor->left = 0;
+ monitor->top = 0;
+ monitor->right = displayWidth - 1;
+ monitor->bottom = displayHeight - 1;
+ monitor->flags = 1;
+ }
+
+ errno = 0;
+ return numMonitors;
+}
+
+static int x11_shadow_subsystem_init(rdpShadowSubsystem* sub)
+{
+ int pf_count = 0;
+ int vi_count = 0;
+ int nextensions = 0;
+ char** extensions = NULL;
+ XVisualInfo* vi = NULL;
+ XVisualInfo* vis = NULL;
+ XVisualInfo template = { 0 };
+ XPixmapFormatValues* pf = NULL;
+ XPixmapFormatValues* pfs = NULL;
+
+ x11ShadowSubsystem* subsystem = (x11ShadowSubsystem*)sub;
+
+ if (!subsystem)
+ return -1;
+
+ subsystem->common.numMonitors = x11_shadow_enum_monitors(subsystem->common.monitors, 16);
+ x11_shadow_subsystem_base_init(subsystem);
+
+ if ((subsystem->depth != 24) && (subsystem->depth != 32))
+ {
+ WLog_ERR(TAG, "unsupported X11 server color depth: %" PRIu32, subsystem->depth);
+ return -1;
+ }
+
+ extensions = XListExtensions(subsystem->display, &nextensions);
+
+ if (!extensions || (nextensions < 0))
+ return -1;
+
+ for (int i = 0; i < nextensions; i++)
+ {
+ if (strcmp(extensions[i], "Composite") == 0)
+ subsystem->composite = TRUE;
+ }
+
+ XFreeExtensionList(extensions);
+
+ if (subsystem->composite)
+ subsystem->use_xdamage = FALSE;
+
+ pfs = XListPixmapFormats(subsystem->display, &pf_count);
+
+ if (!pfs)
+ {
+ WLog_ERR(TAG, "XListPixmapFormats failed");
+ return -1;
+ }
+
+ for (int i = 0; i < pf_count; i++)
+ {
+ pf = pfs + i;
+
+ if (pf->depth == (INT64)subsystem->depth)
+ {
+ subsystem->bpp = pf->bits_per_pixel;
+ subsystem->scanline_pad = pf->scanline_pad;
+ break;
+ }
+ }
+
+ XFree(pfs);
+ template.class = TrueColor;
+ template.screen = subsystem->number;
+ vis = XGetVisualInfo(subsystem->display, VisualClassMask | VisualScreenMask, &template,
+ &vi_count);
+
+ if (!vis)
+ {
+ WLog_ERR(TAG, "XGetVisualInfo failed");
+ return -1;
+ }
+
+ for (int i = 0; i < vi_count; i++)
+ {
+ vi = vis + i;
+
+ if (vi->depth == (INT64)subsystem->depth)
+ {
+ subsystem->visual = vi->visual;
+ break;
+ }
+ }
+
+ XFree(vis);
+ XSelectInput(subsystem->display, subsystem->root_window, SubstructureNotifyMask);
+ subsystem->cursorMaxWidth = 256;
+ subsystem->cursorMaxHeight = 256;
+ subsystem->cursorPixels =
+ winpr_aligned_malloc(subsystem->cursorMaxWidth * subsystem->cursorMaxHeight * 4ull, 16);
+
+ if (!subsystem->cursorPixels)
+ return -1;
+
+ x11_shadow_query_cursor(subsystem, TRUE);
+
+ if (subsystem->use_xfixes)
+ {
+ if (x11_shadow_xfixes_init(subsystem) < 0)
+ subsystem->use_xfixes = FALSE;
+ }
+
+ if (subsystem->use_xinerama)
+ {
+ if (x11_shadow_xinerama_init(subsystem) < 0)
+ subsystem->use_xinerama = FALSE;
+ }
+
+ if (subsystem->use_xshm)
+ {
+ if (x11_shadow_xshm_init(subsystem) < 0)
+ subsystem->use_xshm = FALSE;
+ }
+
+ if (subsystem->use_xdamage)
+ {
+ if (x11_shadow_xdamage_init(subsystem) < 0)
+ subsystem->use_xdamage = FALSE;
+ }
+
+ if (!(subsystem->common.event =
+ CreateFileDescriptorEvent(NULL, FALSE, FALSE, subsystem->xfds, WINPR_FD_READ)))
+ return -1;
+
+ {
+ MONITOR_DEF* virtualScreen = &(subsystem->common.virtualScreen);
+ virtualScreen->left = 0;
+ virtualScreen->top = 0;
+ WINPR_ASSERT(subsystem->width <= INT32_MAX);
+ WINPR_ASSERT(subsystem->height <= INT32_MAX);
+ virtualScreen->right = (INT32)subsystem->width - 1;
+ virtualScreen->bottom = (INT32)subsystem->height - 1;
+ virtualScreen->flags = 1;
+ WLog_INFO(TAG,
+ "X11 Extensions: XFixes: %" PRId32 " Xinerama: %" PRId32 " XDamage: %" PRId32
+ " XShm: %" PRId32 "",
+ subsystem->use_xfixes, subsystem->use_xinerama, subsystem->use_xdamage,
+ subsystem->use_xshm);
+ }
+ return 1;
+}
+
+static int x11_shadow_subsystem_uninit(rdpShadowSubsystem* sub)
+{
+ x11ShadowSubsystem* subsystem = (x11ShadowSubsystem*)sub;
+
+ if (!subsystem)
+ return -1;
+
+ if (subsystem->display)
+ {
+ XCloseDisplay(subsystem->display);
+ subsystem->display = NULL;
+ }
+
+ if (subsystem->common.event)
+ {
+ CloseHandle(subsystem->common.event);
+ subsystem->common.event = NULL;
+ }
+
+ if (subsystem->cursorPixels)
+ {
+ winpr_aligned_free(subsystem->cursorPixels);
+ subsystem->cursorPixels = NULL;
+ }
+
+ return 1;
+}
+
+static int x11_shadow_subsystem_start(rdpShadowSubsystem* sub)
+{
+ x11ShadowSubsystem* subsystem = (x11ShadowSubsystem*)sub;
+
+ if (!subsystem)
+ return -1;
+
+ if (!(subsystem->thread =
+ CreateThread(NULL, 0, x11_shadow_subsystem_thread, (void*)subsystem, 0, NULL)))
+ {
+ WLog_ERR(TAG, "Failed to create thread");
+ return -1;
+ }
+
+ return 1;
+}
+
+static int x11_shadow_subsystem_stop(rdpShadowSubsystem* sub)
+{
+ x11ShadowSubsystem* subsystem = (x11ShadowSubsystem*)sub;
+
+ if (!subsystem)
+ return -1;
+
+ if (subsystem->thread)
+ {
+ if (MessageQueue_PostQuit(subsystem->common.MsgPipe->In, 0))
+ WaitForSingleObject(subsystem->thread, INFINITE);
+
+ CloseHandle(subsystem->thread);
+ subsystem->thread = NULL;
+ }
+
+ return 1;
+}
+
+static rdpShadowSubsystem* x11_shadow_subsystem_new(void)
+{
+ x11ShadowSubsystem* subsystem = NULL;
+ subsystem = (x11ShadowSubsystem*)calloc(1, sizeof(x11ShadowSubsystem));
+
+ if (!subsystem)
+ return NULL;
+
+#ifdef WITH_PAM
+ subsystem->common.Authenticate = x11_shadow_pam_authenticate;
+#endif
+ subsystem->common.SynchronizeEvent = x11_shadow_input_synchronize_event;
+ subsystem->common.KeyboardEvent = x11_shadow_input_keyboard_event;
+ subsystem->common.UnicodeKeyboardEvent = x11_shadow_input_unicode_keyboard_event;
+ subsystem->common.MouseEvent = x11_shadow_input_mouse_event;
+ subsystem->common.ExtendedMouseEvent = x11_shadow_input_extended_mouse_event;
+ subsystem->composite = FALSE;
+ subsystem->use_xshm = FALSE; /* temporarily disabled */
+ subsystem->use_xfixes = TRUE;
+ subsystem->use_xdamage = FALSE;
+ subsystem->use_xinerama = TRUE;
+ return (rdpShadowSubsystem*)subsystem;
+}
+
+static void x11_shadow_subsystem_free(rdpShadowSubsystem* subsystem)
+{
+ if (!subsystem)
+ return;
+
+ x11_shadow_subsystem_uninit(subsystem);
+ free(subsystem);
+}
+
+FREERDP_ENTRY_POINT(FREERDP_API const char* ShadowSubsystemName(void))
+{
+ return "X11";
+}
+
+FREERDP_ENTRY_POINT(FREERDP_API int ShadowSubsystemEntry(RDP_SHADOW_ENTRY_POINTS* pEntryPoints))
+{
+ if (!pEntryPoints)
+ return -1;
+
+ pEntryPoints->New = x11_shadow_subsystem_new;
+ pEntryPoints->Free = x11_shadow_subsystem_free;
+ pEntryPoints->Init = x11_shadow_subsystem_init;
+ pEntryPoints->Uninit = x11_shadow_subsystem_uninit;
+ pEntryPoints->Start = x11_shadow_subsystem_start;
+ pEntryPoints->Stop = x11_shadow_subsystem_stop;
+ pEntryPoints->EnumMonitors = x11_shadow_enum_monitors;
+ return 1;
+}
diff --git a/server/shadow/X11/x11_shadow.h b/server/shadow/X11/x11_shadow.h
new file mode 100644
index 0000000..aca2d63
--- /dev/null
+++ b/server/shadow/X11/x11_shadow.h
@@ -0,0 +1,114 @@
+/**
+ * FreeRDP: A Remote Desktop Protocol Implementation
+ *
+ * Copyright 2011-2014 Marc-Andre Moreau <marcandre.moreau@gmail.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FREERDP_SERVER_SHADOW_X11_H
+#define FREERDP_SERVER_SHADOW_X11_H
+
+#include <freerdp/server/shadow.h>
+
+typedef struct x11_shadow_subsystem x11ShadowSubsystem;
+
+#include <winpr/crt.h>
+#include <winpr/synch.h>
+#include <winpr/thread.h>
+#include <winpr/stream.h>
+#include <winpr/collections.h>
+
+#include <X11/Xlib.h>
+
+#ifdef WITH_XSHM
+#include <X11/extensions/XShm.h>
+#endif
+
+#ifdef WITH_XFIXES
+#include <X11/extensions/Xfixes.h>
+#endif
+
+#ifdef WITH_XTEST
+#include <X11/extensions/XTest.h>
+#endif
+
+#ifdef WITH_XDAMAGE
+#include <X11/extensions/Xdamage.h>
+#endif
+
+#ifdef WITH_XINERAMA
+#include <X11/extensions/Xinerama.h>
+#endif
+
+struct x11_shadow_subsystem
+{
+ rdpShadowSubsystem common;
+
+ HANDLE thread;
+
+ UINT32 bpp;
+ int xfds;
+ UINT32 depth;
+ UINT32 width;
+ UINT32 height;
+ int number;
+ XImage* image;
+ Screen* screen;
+ Visual* visual;
+ Display* display;
+ UINT32 scanline_pad;
+ BOOL composite;
+
+ BOOL use_xshm;
+ BOOL use_xfixes;
+ BOOL use_xdamage;
+ BOOL use_xinerama;
+
+ XImage* fb_image;
+ Pixmap fb_pixmap;
+ Window root_window;
+ XShmSegmentInfo fb_shm_info;
+
+ UINT32 cursorHotX;
+ UINT32 cursorHotY;
+ UINT32 cursorWidth;
+ UINT32 cursorHeight;
+ UINT32 cursorId;
+ BYTE* cursorPixels;
+ UINT32 cursorMaxWidth;
+ UINT32 cursorMaxHeight;
+ rdpShadowClient* lastMouseClient;
+
+#ifdef WITH_XDAMAGE
+ GC xshm_gc;
+ Damage xdamage;
+ int xdamage_notify_event;
+ XserverRegion xdamage_region;
+#endif
+
+#ifdef WITH_XFIXES
+ int xfixes_cursor_notify_event;
+#endif
+};
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* FREERDP_SERVER_SHADOW_X11_H */