diff options
Diffstat (limited to '')
-rw-r--r-- | client/SDL/sdl_touch.cpp | 285 |
1 files changed, 285 insertions, 0 deletions
diff --git a/client/SDL/sdl_touch.cpp b/client/SDL/sdl_touch.cpp new file mode 100644 index 0000000..81fcbfb --- /dev/null +++ b/client/SDL/sdl_touch.cpp @@ -0,0 +1,285 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * FreeRDP SDL touch/mouse input + * + * Copyright 2022 Armin Novak <armin.novak@thincast.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. + */ + +#include <freerdp/config.h> + +#include "sdl_touch.hpp" +#include "sdl_freerdp.hpp" + +#include <winpr/wtypes.h> +#include <winpr/assert.h> + +#include <freerdp/freerdp.h> +#include <freerdp/gdi/gdi.h> + +#include <SDL.h> + +#define TAG CLIENT_TAG("SDL.touch") + +BOOL sdl_scale_coordinates(SdlContext* sdl, Uint32 windowId, INT32* px, INT32* py, + BOOL fromLocalToRDP, BOOL applyOffset) +{ + rdpGdi* gdi = nullptr; + double sx = 1.0; + double sy = 1.0; + + if (!sdl || !px || !py || !sdl->context()->gdi) + return FALSE; + + WINPR_ASSERT(sdl->context()->gdi); + WINPR_ASSERT(sdl->context()->settings); + + gdi = sdl->context()->gdi; + + // TODO: Make this multimonitor ready! + // TODO: Need to find the primary monitor, get the scale + // TODO: Need to find the destination monitor, get the scale + // TODO: All intermediate monitors, get the scale + + int offset_x = 0; + int offset_y = 0; + for (const auto& it : sdl->windows) + { + auto& window = it.second; + const auto id = window.id(); + if (id != windowId) + { + continue; + } + + auto size = window.rect(); + + sx = size.w / static_cast<double>(gdi->width); + sy = size.h / static_cast<double>(gdi->height); + offset_x = window.offsetX(); + offset_y = window.offsetY(); + break; + } + + if (freerdp_settings_get_bool(sdl->context()->settings, FreeRDP_SmartSizing)) + { + if (!fromLocalToRDP) + { + *px = static_cast<INT32>(*px * sx); + *py = static_cast<INT32>(*py * sy); + } + else + { + *px = static_cast<INT32>(*px / sx); + *py = static_cast<INT32>(*py / sy); + } + } + else if (applyOffset) + { + *px -= offset_x; + *py -= offset_y; + } + + return TRUE; +} + +static BOOL sdl_get_touch_scaled(SdlContext* sdl, const SDL_TouchFingerEvent* ev, INT32* px, + INT32* py, BOOL local) +{ + Uint32 windowID = 0; + + WINPR_ASSERT(sdl); + WINPR_ASSERT(ev); + WINPR_ASSERT(px); + WINPR_ASSERT(py); + +#if SDL_VERSION_ATLEAST(2, 0, 12) + SDL_Window* window = SDL_GetWindowFromID(ev->windowID); +#else + SDL_Window* window = SDL_GetMouseFocus(); +#endif + + if (!window) + return FALSE; + + windowID = SDL_GetWindowID(window); + SDL_Surface* surface = SDL_GetWindowSurface(window); + if (!surface) + return FALSE; + + // TODO: Add the offset of the surface in the global coordinates + *px = static_cast<INT32>(ev->x * static_cast<float>(surface->w)); + *py = static_cast<INT32>(ev->y * static_cast<float>(surface->h)); + return sdl_scale_coordinates(sdl, windowID, px, py, local, TRUE); +} + +static BOOL send_mouse_wheel(SdlContext* sdl, UINT16 flags, INT32 avalue) +{ + WINPR_ASSERT(sdl); + if (avalue < 0) + { + flags |= PTR_FLAGS_WHEEL_NEGATIVE; + avalue = -avalue; + } + + while (avalue > 0) + { + const UINT16 cval = (avalue > 0xFF) ? 0xFF : static_cast<UINT16>(avalue); + UINT16 cflags = flags | cval; + /* Convert negative values to 9bit twos complement */ + if (flags & PTR_FLAGS_WHEEL_NEGATIVE) + cflags = (flags & 0xFF00) | (0x100 - cval); + if (!freerdp_client_send_wheel_event(sdl->common(), cflags)) + return FALSE; + + avalue -= cval; + } + return TRUE; +} + +static UINT32 sdl_scale_pressure(const float pressure) +{ + const float val = pressure * 0x400; /* [MS-RDPEI] 2.2.3.3.1.1 RDPINPUT_TOUCH_CONTACT */ + if (val < 0.0f) + return 0; + if (val > 0x400) + return 0x400; + return static_cast<UINT32>(val); +} + +BOOL sdl_handle_touch_up(SdlContext* sdl, const SDL_TouchFingerEvent* ev) +{ + WINPR_ASSERT(sdl); + WINPR_ASSERT(ev); + + INT32 x = 0; + INT32 y = 0; + if (!sdl_get_touch_scaled(sdl, ev, &x, &y, TRUE)) + return FALSE; + return freerdp_client_handle_touch(sdl->common(), FREERDP_TOUCH_UP | FREERDP_TOUCH_HAS_PRESSURE, + static_cast<INT32>(ev->fingerId), + sdl_scale_pressure(ev->pressure), x, y); +} + +BOOL sdl_handle_touch_down(SdlContext* sdl, const SDL_TouchFingerEvent* ev) +{ + WINPR_ASSERT(sdl); + WINPR_ASSERT(ev); + + INT32 x = 0; + INT32 y = 0; + if (!sdl_get_touch_scaled(sdl, ev, &x, &y, TRUE)) + return FALSE; + return freerdp_client_handle_touch( + sdl->common(), FREERDP_TOUCH_DOWN | FREERDP_TOUCH_HAS_PRESSURE, + static_cast<INT32>(ev->fingerId), sdl_scale_pressure(ev->pressure), x, y); +} + +BOOL sdl_handle_touch_motion(SdlContext* sdl, const SDL_TouchFingerEvent* ev) +{ + WINPR_ASSERT(sdl); + WINPR_ASSERT(ev); + + INT32 x = 0; + INT32 y = 0; + if (!sdl_get_touch_scaled(sdl, ev, &x, &y, TRUE)) + return FALSE; + return freerdp_client_handle_touch( + sdl->common(), FREERDP_TOUCH_MOTION | FREERDP_TOUCH_HAS_PRESSURE, + static_cast<INT32>(ev->fingerId), sdl_scale_pressure(ev->pressure), x, y); +} + +BOOL sdl_handle_mouse_motion(SdlContext* sdl, const SDL_MouseMotionEvent* ev) +{ + WINPR_ASSERT(sdl); + WINPR_ASSERT(ev); + + sdl->input.mouse_focus(ev->windowID); + const BOOL relative = freerdp_client_use_relative_mouse_events(sdl->common()); + INT32 x = relative ? ev->xrel : ev->x; + INT32 y = relative ? ev->yrel : ev->y; + sdl_scale_coordinates(sdl, ev->windowID, &x, &y, TRUE, TRUE); + return freerdp_client_send_button_event(sdl->common(), relative, PTR_FLAGS_MOVE, x, y); +} + +BOOL sdl_handle_mouse_wheel(SdlContext* sdl, const SDL_MouseWheelEvent* ev) +{ + WINPR_ASSERT(sdl); + WINPR_ASSERT(ev); + + const BOOL flipped = (ev->direction == SDL_MOUSEWHEEL_FLIPPED); + const INT32 x = ev->x * (flipped ? -1 : 1) * 0x78; + const INT32 y = ev->y * (flipped ? -1 : 1) * 0x78; + UINT16 flags = 0; + + if (y != 0) + { + flags |= PTR_FLAGS_WHEEL; + send_mouse_wheel(sdl, flags, y); + } + + if (x != 0) + { + flags |= PTR_FLAGS_HWHEEL; + send_mouse_wheel(sdl, flags, x); + } + return TRUE; +} + +BOOL sdl_handle_mouse_button(SdlContext* sdl, const SDL_MouseButtonEvent* ev) +{ + UINT16 flags = 0; + UINT16 xflags = 0; + + WINPR_ASSERT(sdl); + WINPR_ASSERT(ev); + + if (ev->state == SDL_PRESSED) + { + flags |= PTR_FLAGS_DOWN; + xflags |= PTR_XFLAGS_DOWN; + } + + switch (ev->button) + { + case 1: + flags |= PTR_FLAGS_BUTTON1; + break; + case 2: + flags |= PTR_FLAGS_BUTTON3; + break; + case 3: + flags |= PTR_FLAGS_BUTTON2; + break; + case 4: + xflags |= PTR_XFLAGS_BUTTON1; + break; + case 5: + xflags |= PTR_XFLAGS_BUTTON2; + break; + default: + break; + } + + const BOOL relative = freerdp_client_use_relative_mouse_events(sdl->common()); + INT32 x = relative ? 0 : ev->x; + INT32 y = relative ? 0 : ev->y; + sdl_scale_coordinates(sdl, ev->windowID, &x, &y, TRUE, TRUE); + if ((flags & (~PTR_FLAGS_DOWN)) != 0) + return freerdp_client_send_button_event(sdl->common(), relative, flags, x, y); + else if ((xflags & (~PTR_XFLAGS_DOWN)) != 0) + return freerdp_client_send_extended_button_event(sdl->common(), relative, xflags, x, y); + else + return FALSE; +} |