FreeRDP
Loading...
Searching...
No Matches
SDL3/sdl_pointer.cpp
1
20#include <algorithm>
21
22#include <freerdp/config.h>
23
24#include <freerdp/gdi/gdi.h>
25
26#include "sdl_pointer.hpp"
27#include "sdl_context.hpp"
28#include "sdl_touch.hpp"
29#include "sdl_utils.hpp"
30
31#include <SDL3/SDL_mouse.h>
32
33struct sdlPointer
34{
35 rdpPointer pointer{};
36 SDL_Cursor* cursor = nullptr;
37 SDL_Surface* image = nullptr;
38 size_t size = 0;
39 BYTE* data = nullptr;
40
41 sdlPointer(const sdlPointer& other) = delete;
42 sdlPointer(sdlPointer&& other) = delete;
43 auto operator=(const sdlPointer& other) = delete;
44 auto operator=(sdlPointer&& other) = delete;
45 ~sdlPointer() = delete;
46
47 bool update(rdpContext* context)
48 {
49 assert(context);
50 assert(context->gdi);
51
52 size = 4ull * pointer.width * pointer.height;
53 winpr_aligned_free(data);
54 data = static_cast<BYTE*>(winpr_aligned_malloc(size, 16));
55
56 if (!data)
57 return false;
58
59 return freerdp_image_copy_from_pointer_data(
60 data, context->gdi->dstFormat, 0, 0, 0, pointer.width, pointer.height,
61 pointer.xorMaskData, pointer.lengthXorMask, pointer.andMaskData, pointer.lengthAndMask,
62 pointer.xorBpp, &context->gdi->palette);
63 }
64};
65
66[[nodiscard]] static BOOL sdl_Pointer_New(rdpContext* context, rdpPointer* pointer)
67{
68 auto ptr = reinterpret_cast<sdlPointer*>(pointer);
69 auto sdl = get_context(context);
70
71 WINPR_ASSERT(sdl);
72 std::unique_lock lock(sdl->lock());
73 if (!ptr)
74 return FALSE;
75 sdl->pointers().push_back(pointer);
76
77 return ptr->update(context);
78}
79
80static void sdl_Pointer_Clear(sdlPointer* ptr)
81{
82 WINPR_ASSERT(ptr);
83 SDL_DestroyCursor(ptr->cursor);
84 SDL_DestroySurface(ptr->image);
85 ptr->cursor = nullptr;
86 ptr->image = nullptr;
87}
88
89static void sdl_Pointer_Free(WINPR_ATTR_UNUSED rdpContext* context, rdpPointer* pointer)
90{
91 auto sdl = get_context(context);
92
93 WINPR_ASSERT(sdl);
94 std::unique_lock lock(sdl->lock());
95 auto it = std::remove(sdl->pointers().begin(), sdl->pointers().end(), pointer);
96 sdl->pointers().erase(it, sdl->pointers().end());
97
98 sdl_Pointer_FreeCopy(pointer);
99}
100
101void sdl_Pointer_FreeCopy(rdpPointer* pointer)
102{
103 auto ptr = reinterpret_cast<sdlPointer*>(pointer);
104
105 if (!ptr)
106 return;
107
108 sdl_Pointer_Clear(ptr);
109 winpr_aligned_free(ptr->data);
110 ptr->data = nullptr;
111}
112
113[[nodiscard]] static BOOL sdl_Pointer_SetDefault(rdpContext* context)
114{
115 WINPR_UNUSED(context);
116
117 return sdl_push_user_event(SDL_EVENT_USER_POINTER_DEFAULT);
118}
119
120[[nodiscard]] static BOOL sdl_Pointer_Set(rdpContext* context, rdpPointer* pointer)
121{
122 WINPR_UNUSED(context);
123 return sdl_push_user_event(SDL_EVENT_USER_POINTER_SET, pointer);
124}
125
126bool sdl_Pointer_Set_Process(SdlContext* sdl)
127{
128 WINPR_ASSERT(sdl);
129
130 std::unique_lock lock(sdl->lock());
131 auto context = sdl->context();
132 auto pointer = sdl->cursor();
133 auto ptr = reinterpret_cast<sdlPointer*>(pointer);
134 if (!ptr)
135 return true;
136
137 rdpGdi* gdi = context->gdi;
138 WINPR_ASSERT(gdi);
139
140 auto ix = static_cast<float>(pointer->xPos);
141 auto iy = static_cast<float>(pointer->yPos);
142 auto isw = static_cast<float>(pointer->width);
143 auto ish = static_cast<float>(pointer->height);
144
145 auto window = SDL_GetMouseFocus();
146 if (!window)
147 return sdl_Pointer_SetDefault(context);
148
149 const Uint32 id = SDL_GetWindowID(window);
150
151 const SDL_FRect orig{ ix, iy, isw, ish };
152 const auto pos = sdl->pixelToScreen(id, orig, true);
153 WLog_Print(sdl->getWLog(), WLOG_DEBUG, "cursor scale: pixel:%s, display:%s",
154 sdl::utils::toString(orig).c_str(), sdl::utils::toString(pos).c_str());
155
156 sdl_Pointer_Clear(ptr);
157
158 ptr->image =
159 SDL_CreateSurface(static_cast<int>(orig.w), static_cast<int>(orig.h), sdl->pixelFormat());
160 if (!ptr->image)
161 {
162 WLog_Print(sdl->getWLog(), WLOG_ERROR, "SDL_CreateSurface failed");
163 return false;
164 }
165
166 auto data = static_cast<const BYTE*>(ptr->data);
167 if (data)
168 {
169 if (!SDL_LockSurface(ptr->image))
170 {
171 WLog_Print(sdl->getWLog(), WLOG_ERROR, "SDL_LockSurface failed");
172 return false;
173 }
174
175 auto pixels = static_cast<BYTE*>(ptr->image->pixels);
176 const BOOL rc = freerdp_image_scale(
177 pixels, gdi->dstFormat, static_cast<UINT32>(ptr->image->pitch), 0, 0,
178 static_cast<UINT32>(ptr->image->w), static_cast<UINT32>(ptr->image->h), data,
179 gdi->dstFormat, 0, 0, 0, static_cast<UINT32>(isw), static_cast<UINT32>(ish));
180 SDL_UnlockSurface(ptr->image);
181 if (!rc)
182 {
183 WLog_Print(sdl->getWLog(), WLOG_ERROR, "freerdp_image_scale failed");
184 return false;
185 }
186 }
187
188 // create a cursor image in 100% display scale to trick SDL into creating the cursor with the
189 // correct size
190 auto fw = sdl->getFirstWindow();
191 if (!fw)
192 {
193 WLog_Print(sdl->getWLog(), WLOG_ERROR, "sdl->getFirstWindow() nullptr");
194 return false;
195 }
196
197 const auto w = static_cast<int>(pos.w);
198 const auto h = static_cast<int>(pos.h);
199 std::unique_ptr<SDL_Surface, void (*)(SDL_Surface*)> normal{
200 SDL_CreateSurface(w, h, ptr->image->format), SDL_DestroySurface
201 };
202 assert(normal);
203 if (!SDL_BlitSurfaceScaled(ptr->image, nullptr, normal.get(), nullptr,
204 SDL_ScaleMode::SDL_SCALEMODE_LINEAR))
205 {
206 WLog_Print(sdl->getWLog(), WLOG_ERROR, "SDL_BlitSurfaceScaled failed");
207 return false;
208 }
209 if (!SDL_AddSurfaceAlternateImage(normal.get(), ptr->image))
210 {
211 WLog_Print(sdl->getWLog(), WLOG_ERROR, "SDL_AddSurfaceAlternateImage failed");
212 return false;
213 }
214
215 auto x = static_cast<int>(pos.x);
216 auto y = static_cast<int>(pos.y);
217 if (x >= w)
218 x = w - 1;
219 if (y >= h)
220 y = h - 1;
221
222 ptr->cursor = SDL_CreateColorCursor(normal.get(), x, y);
223 if (!ptr->cursor)
224 {
225 WLog_Print(sdl->getWLog(), WLOG_ERROR, "SDL_CreateColorCursor(display:%s, pixel:%s} failed",
226 sdl::utils::toString(pos).c_str(), sdl::utils::toString(orig).c_str());
227 return false;
228 }
229
230 if (!SDL_SetCursor(ptr->cursor))
231 {
232 WLog_Print(sdl->getWLog(), WLOG_ERROR, "SDL_SetCursor failed");
233 return false;
234 }
235 if (!SDL_ShowCursor())
236 {
237 WLog_Print(sdl->getWLog(), WLOG_ERROR, "SDL_ShowCursor failed");
238 return false;
239 }
240 sdl->setHasCursor(true);
241 return true;
242}
243
244[[nodiscard]] static BOOL sdl_Pointer_SetNull(rdpContext* context)
245{
246 WINPR_UNUSED(context);
247
248 return sdl_push_user_event(SDL_EVENT_USER_POINTER_NULL);
249}
250
251[[nodiscard]] static BOOL sdl_Pointer_SetPosition(rdpContext* context, UINT32 x, UINT32 y)
252{
253 WINPR_UNUSED(context);
254 WINPR_ASSERT(context);
255
256 return sdl_push_user_event(SDL_EVENT_USER_POINTER_POSITION, x, y);
257}
258
259bool sdl_register_pointer(rdpGraphics* graphics)
260{
261 const rdpPointer pointer = { sizeof(sdlPointer),
262 sdl_Pointer_New,
263 sdl_Pointer_Free,
264 sdl_Pointer_Set,
265 sdl_Pointer_SetNull,
266 sdl_Pointer_SetDefault,
267 sdl_Pointer_SetPosition,
268 {},
269 0,
270 0,
271 0,
272 0,
273 0,
274 0,
275 0,
276 nullptr,
277 nullptr,
278 {} };
279 graphics_register_pointer(graphics, &pointer);
280 return true;
281}
282
283rdpPointer* sdl_Pointer_Copy(const rdpPointer* pointer)
284{
285 auto ptr = reinterpret_cast<const sdlPointer*>(pointer);
286 if (!pointer)
287 return nullptr;
288
289 auto copy = static_cast<sdlPointer*>(calloc(1, sizeof(sdlPointer)));
290 if (!copy)
291 return nullptr;
292
293 copy->pointer.xPos = pointer->xPos;
294 copy->pointer.yPos = pointer->yPos;
295 copy->pointer.width = pointer->width;
296 copy->pointer.height = pointer->height;
297 copy->pointer.xorBpp = pointer->xorBpp;
298 if (ptr->size > 0)
299 {
300 copy->data = static_cast<BYTE*>(winpr_aligned_malloc(ptr->size, 32));
301 if (!copy)
302 {
303 free(copy);
304 return nullptr;
305 }
306 copy->size = ptr->size;
307 memcpy(copy->data, ptr->data, copy->size);
308 }
309 return &copy->pointer;
310}