diff --git a/include/freerdp/server/shadow.h b/include/freerdp/server/shadow.h index 839028435..d92c021f0 100644 --- a/include/freerdp/server/shadow.h +++ b/include/freerdp/server/shadow.h @@ -92,6 +92,10 @@ extern "C" UINT16 code); typedef BOOL (*pfnShadowMouseEvent)(rdpShadowSubsystem* subsystem, rdpShadowClient* client, UINT16 flags, UINT16 x, UINT16 y); + typedef BOOL (*pfnShadowRelMouseEvent)(rdpShadowSubsystem* subsystem, rdpShadowClient* client, + UINT16 flags, INT16 xDelta, + INT16 yDelta); /** @since version 3.15.0 */ + typedef BOOL (*pfnShadowExtendedMouseEvent)(rdpShadowSubsystem* subsystem, rdpShadowClient* client, UINT16 flags, UINT16 x, UINT16 y); @@ -244,6 +248,8 @@ extern "C" pfnShadowClientCapabilities ClientCapabilities; rdpShadowServer* server; + + pfnShadowRelMouseEvent RelMouseEvent; /** @since version 3.15.0 */ }; /* Definition of message between subsystem and clients */ diff --git a/server/shadow/X11/x11_shadow.c b/server/shadow/X11/x11_shadow.c index ad206f560..4d0a55885 100644 --- a/server/shadow/X11/x11_shadow.c +++ b/server/shadow/X11/x11_shadow.c @@ -248,7 +248,8 @@ static BOOL x11_shadow_input_keyboard_event(rdpShadowSubsystem* subsystem, rdpSh XFlush(x11->display); XUnlockDisplay(x11->display); } - +#else + WLog_WARN(TAG, "KeyboardEvent not supported by backend, ignoring"); #endif return TRUE; } @@ -336,6 +337,65 @@ static BOOL x11_shadow_input_mouse_event(rdpShadowSubsystem* subsystem, rdpShado XTestGrabControl(x11->display, False); XFlush(x11->display); XUnlockDisplay(x11->display); +#else + WLog_WARN(TAG, "MouseEvent not supported by backend, ignoring"); +#endif + return TRUE; +} + +static BOOL x11_shadow_input_rel_mouse_event(rdpShadowSubsystem* subsystem, rdpShadowClient* client, + UINT16 flags, INT16 xDelta, INT16 yDelta) +{ +#ifdef WITH_XTEST + x11ShadowSubsystem* x11 = (x11ShadowSubsystem*)subsystem; + WINPR_ASSERT(x11); + + unsigned int button = 0; + BOOL down = FALSE; + + if (!subsystem || !client) + return FALSE; + + rdpShadowServer* server = subsystem->server; + + if (!server) + return FALSE; + + rdpShadowSurface* surface = server->surface; + + if (!surface) + return FALSE; + + x11->lastMouseClient = client; + + XLockDisplay(x11->display); + XTestGrabControl(x11->display, True); + + if (flags & PTR_FLAGS_MOVE) + XTestFakeRelativeMotionEvent(x11->display, xDelta, yDelta, 0); + + if (flags & PTR_FLAGS_BUTTON1) + button = 1; + else if (flags & PTR_FLAGS_BUTTON2) + button = 3; + else if (flags & PTR_FLAGS_BUTTON3) + button = 2; + else if (flags & PTR_XFLAGS_BUTTON1) + button = 4; + else if (flags & PTR_XFLAGS_BUTTON2) + button = 5; + + 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); +#else + WLog_WARN(TAG, "RelMouseEvent not supported by backend, ignoring"); #endif return TRUE; } @@ -385,6 +445,8 @@ static BOOL x11_shadow_input_extended_mouse_event(rdpShadowSubsystem* subsystem, XTestGrabControl(x11->display, False); XFlush(x11->display); XUnlockDisplay(x11->display); +#else + WLog_WARN(TAG, "ExtendedMouseEvent not supported by backend, ignoring"); #endif return TRUE; } @@ -1480,6 +1542,7 @@ static rdpShadowSubsystem* x11_shadow_subsystem_new(void) 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.RelMouseEvent = x11_shadow_input_rel_mouse_event; subsystem->common.ExtendedMouseEvent = x11_shadow_input_extended_mouse_event; subsystem->composite = FALSE; subsystem->use_xshm = FALSE; /* temporarily disabled */ diff --git a/server/shadow/cli/shadow.c b/server/shadow/cli/shadow.c index adfc6ec10..464abd9e4 100644 --- a/server/shadow/cli/shadow.c +++ b/server/shadow/cli/shadow.c @@ -52,6 +52,8 @@ int main(int argc, char** argv) "Select or list monitors" }, { "max-connections", COMMAND_LINE_VALUE_REQUIRED, "", 0, NULL, -1, NULL, "maximum connections allowed to server, 0 to deactivate" }, + { "mouse-relative", COMMAND_LINE_VALUE_BOOL, NULL, NULL, NULL, -1, NULL, + "enable support for relative mouse events" }, { "rect", COMMAND_LINE_VALUE_REQUIRED, "", NULL, NULL, -1, NULL, "Select rectangle within monitor to share" }, { "auth", COMMAND_LINE_VALUE_BOOL, NULL, BoolValueTrue, NULL, -1, NULL, @@ -140,7 +142,6 @@ int main(int argc, char** argv) !freerdp_settings_set_bool(settings, FreeRDP_GfxProgressiveV2, TRUE)) goto fail; - /* TODO: We do not implement relative mouse callbacks, so deactivate it for now */ if (!freerdp_settings_set_bool(settings, FreeRDP_MouseUseRelativeMove, FALSE) || !freerdp_settings_set_bool(settings, FreeRDP_HasRelativeMouseEvent, FALSE)) goto fail; diff --git a/server/shadow/shadow_input.c b/server/shadow/shadow_input.c index 97268b8ae..4308ad300 100644 --- a/server/shadow/shadow_input.c +++ b/server/shadow/shadow_input.c @@ -16,15 +16,24 @@ * limitations under the License. */ +#include #include +#include #include "shadow.h" +#define TAG SERVER_TAG("shadow.input") + static BOOL shadow_input_synchronize_event(rdpInput* input, UINT32 flags) { + WINPR_ASSERT(input); rdpShadowClient* client = (rdpShadowClient*)input->context; + WINPR_ASSERT(client); + WINPR_ASSERT(client->server); rdpShadowSubsystem* subsystem = client->server->subsystem; + WINPR_ASSERT(subsystem); + WLog_DBG(TAG, "[%s] flags=0x%04" PRIx16, client->mayInteract ? "use" : "discard", flags); if (!client->mayInteract) return TRUE; @@ -33,9 +42,14 @@ static BOOL shadow_input_synchronize_event(rdpInput* input, UINT32 flags) static BOOL shadow_input_keyboard_event(rdpInput* input, UINT16 flags, UINT8 code) { + WINPR_ASSERT(input); rdpShadowClient* client = (rdpShadowClient*)input->context; + WINPR_ASSERT(client); + WINPR_ASSERT(client->server); rdpShadowSubsystem* subsystem = client->server->subsystem; + WINPR_ASSERT(subsystem); + WLog_DBG(TAG, "[%s] flags=0x%04" PRIx16, client->mayInteract ? "use" : "discard", flags); if (!client->mayInteract) return TRUE; @@ -44,9 +58,14 @@ static BOOL shadow_input_keyboard_event(rdpInput* input, UINT16 flags, UINT8 cod static BOOL shadow_input_unicode_keyboard_event(rdpInput* input, UINT16 flags, UINT16 code) { + WINPR_ASSERT(input); rdpShadowClient* client = (rdpShadowClient*)input->context; + WINPR_ASSERT(client); + WINPR_ASSERT(client->server); rdpShadowSubsystem* subsystem = client->server->subsystem; + WINPR_ASSERT(subsystem); + WLog_DBG(TAG, "[%s] flags=0x%04" PRIx16, client->mayInteract ? "use" : "discard", flags); if (!client->mayInteract) return TRUE; @@ -55,8 +74,12 @@ static BOOL shadow_input_unicode_keyboard_event(rdpInput* input, UINT16 flags, U static BOOL shadow_input_mouse_event(rdpInput* input, UINT16 flags, UINT16 x, UINT16 y) { + WINPR_ASSERT(input); rdpShadowClient* client = (rdpShadowClient*)input->context; + WINPR_ASSERT(client); + WINPR_ASSERT(client->server); rdpShadowSubsystem* subsystem = client->server->subsystem; + WINPR_ASSERT(subsystem); if (client->server->shareSubRect) { @@ -64,7 +87,7 @@ static BOOL shadow_input_mouse_event(rdpInput* input, UINT16 flags, UINT16 x, UI y += client->server->subRect.top; } - if (!(flags & PTR_FLAGS_WHEEL)) + if ((flags & (PTR_FLAGS_WHEEL | PTR_FLAGS_HWHEEL | PTR_FLAGS_WHEEL_NEGATIVE)) == 0) { client->pointerX = x; client->pointerY = y; @@ -78,16 +101,47 @@ static BOOL shadow_input_mouse_event(rdpInput* input, UINT16 flags, UINT16 x, UI } } + WLog_DBG(TAG, "[%s] flags=0x%04" PRIx16 ", x=%" PRIu16 ", y=%" PRIu16, + client->mayInteract ? "use" : "discard", flags, x, y); if (!client->mayInteract) return TRUE; return IFCALLRESULT(TRUE, subsystem->MouseEvent, subsystem, client, flags, x, y); } +static BOOL shadow_input_rel_mouse_event(rdpInput* input, UINT16 flags, INT16 xDelta, INT16 yDelta) +{ + WINPR_ASSERT(input); + + rdpShadowClient* client = (rdpShadowClient*)input->context; + WINPR_ASSERT(client); + WINPR_ASSERT(client->server); + + rdpShadowSubsystem* subsystem = client->server->subsystem; + WINPR_ASSERT(subsystem); + + WLog_DBG(TAG, "[%s] flags=0x%04" PRIx16 ", x=%" PRId16 ", y=%" PRId16, + client->mayInteract ? "use" : "discard", flags, xDelta, yDelta); + const uint16_t mask = PTR_FLAGS_MOVE | PTR_FLAGS_DOWN | PTR_FLAGS_BUTTON1 | PTR_FLAGS_BUTTON2 | + PTR_FLAGS_BUTTON3 | PTR_XFLAGS_BUTTON1 | PTR_XFLAGS_BUTTON2; + if ((flags & mask) != 0) + { + WLog_WARN(TAG, "Unknown flags 0x%04" PRIx16, flags & ~mask); + } + if (!client->mayInteract) + return TRUE; + + return IFCALLRESULT(TRUE, subsystem->RelMouseEvent, subsystem, client, flags, xDelta, yDelta); +} + static BOOL shadow_input_extended_mouse_event(rdpInput* input, UINT16 flags, UINT16 x, UINT16 y) { + WINPR_ASSERT(input); rdpShadowClient* client = (rdpShadowClient*)input->context; + WINPR_ASSERT(client); + WINPR_ASSERT(client->server); rdpShadowSubsystem* subsystem = client->server->subsystem; + WINPR_ASSERT(subsystem); if (client->server->shareSubRect) { @@ -98,6 +152,8 @@ static BOOL shadow_input_extended_mouse_event(rdpInput* input, UINT16 flags, UIN client->pointerX = x; client->pointerY = y; + WLog_DBG(TAG, "[%s] flags=0x%04" PRIx16 ", x=%" PRIu16 ", y=%" PRIu16, + client->mayInteract ? "use" : "discard", flags, x, y); if (!client->mayInteract) return TRUE; @@ -106,9 +162,11 @@ static BOOL shadow_input_extended_mouse_event(rdpInput* input, UINT16 flags, UIN void shadow_input_register_callbacks(rdpInput* input) { + WINPR_ASSERT(input); input->SynchronizeEvent = shadow_input_synchronize_event; input->KeyboardEvent = shadow_input_keyboard_event; input->UnicodeKeyboardEvent = shadow_input_unicode_keyboard_event; input->MouseEvent = shadow_input_mouse_event; input->ExtendedMouseEvent = shadow_input_extended_mouse_event; + input->RelMouseEvent = shadow_input_rel_mouse_event; } diff --git a/server/shadow/shadow_server.c b/server/shadow/shadow_server.c index a4c6eea3d..837ca38d4 100644 --- a/server/shadow/shadow_server.c +++ b/server/shadow/shadow_server.c @@ -244,6 +244,13 @@ int shadow_server_parse_command_line(rdpShadowServer* server, int argc, char** a { server->mayInteract = arg->Value ? TRUE : FALSE; } + CommandLineSwitchCase(arg, "mouse-relative") + { + const BOOL val = arg->Value ? TRUE : FALSE; + if (!freerdp_settings_set_bool(settings, FreeRDP_MouseUseRelativeMove, val) || + !freerdp_settings_set_bool(settings, FreeRDP_HasRelativeMouseEvent, val)) + return fail_at(arg, COMMAND_LINE_ERROR); + } CommandLineSwitchCase(arg, "max-connections") { errno = 0;