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