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 const auto pos = sdl->pixelToScreen(id, orig, true);
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 w = static_cast<int>(pos.w);
185 const auto h = static_cast<int>(pos.h);
186 std::unique_ptr<SDL_Surface, void (*)(SDL_Surface*)> normal{
187 SDL_CreateSurface(w, h, ptr->image->format), SDL_DestroySurface
188 };
189 assert(normal);
190 if (!SDL_BlitSurfaceScaled(ptr->image, nullptr, normal.get(), nullptr,
191 SDL_ScaleMode::SDL_SCALEMODE_LINEAR))
192 {
193 WLog_Print(sdl->getWLog(), WLOG_ERROR, "SDL_BlitSurfaceScaled failed");
194 return false;
195 }
196 if (!SDL_AddSurfaceAlternateImage(normal.get(), ptr->image))
197 {
198 WLog_Print(sdl->getWLog(), WLOG_ERROR, "SDL_AddSurfaceAlternateImage failed");
199 return false;
200 }
201
202 auto x = static_cast<int>(pos.x);
203 auto y = static_cast<int>(pos.y);
204 if (x >= w)
205 x = w - 1;
206 if (y >= h)
207 y = h - 1;
208
209 ptr->cursor = SDL_CreateColorCursor(normal.get(), x, y);
210 if (!ptr->cursor)
211 {
212 WLog_Print(sdl->getWLog(), WLOG_ERROR, "SDL_CreateColorCursor(display:%s, pixel:%s} failed",
213 sdl::utils::toString(pos).c_str(), sdl::utils::toString(orig).c_str());
214 return false;
215 }
216
217 if (!SDL_SetCursor(ptr->cursor))
218 {
219 WLog_Print(sdl->getWLog(), WLOG_ERROR, "SDL_SetCursor failed");
220 return false;
221 }
222 if (!SDL_ShowCursor())
223 {
224 WLog_Print(sdl->getWLog(), WLOG_ERROR, "SDL_ShowCursor failed");
225 return false;
226 }
227 sdl->setHasCursor(true);
228 return true;
229}
230
231[[nodiscard]] static BOOL sdl_Pointer_SetNull(rdpContext* context)
232{
233 WINPR_UNUSED(context);
234
235 return sdl_push_user_event(SDL_EVENT_USER_POINTER_NULL);
236}
237
238[[nodiscard]] static BOOL sdl_Pointer_SetPosition(rdpContext* context, UINT32 x, UINT32 y)
239{
240 WINPR_UNUSED(context);
241 WINPR_ASSERT(context);
242
243 return sdl_push_user_event(SDL_EVENT_USER_POINTER_POSITION, x, y);
244}
245
246bool sdl_register_pointer(rdpGraphics* graphics)
247{
248 const rdpPointer pointer = { sizeof(sdlPointer),
249 sdl_Pointer_New,
250 sdl_Pointer_Free,
251 sdl_Pointer_Set,
252 sdl_Pointer_SetNull,
253 sdl_Pointer_SetDefault,
254 sdl_Pointer_SetPosition,
255 {},
256 0,
257 0,
258 0,
259 0,
260 0,
261 0,
262 0,
263 nullptr,
264 nullptr,
265 {} };
266 graphics_register_pointer(graphics, &pointer);
267 return true;
268}
269
270rdpPointer* sdl_Pointer_Copy(const rdpPointer* pointer)
271{
272 auto ptr = reinterpret_cast<const sdlPointer*>(pointer);
273 if (!pointer)
274 return nullptr;
275
276 auto copy = static_cast<sdlPointer*>(calloc(1, sizeof(sdlPointer)));
277 if (!copy)
278 return nullptr;
279
280 copy->pointer.xPos = pointer->xPos;
281 copy->pointer.yPos = pointer->yPos;
282 copy->pointer.width = pointer->width;
283 copy->pointer.height = pointer->height;
284 copy->pointer.xorBpp = pointer->xorBpp;
285 if (ptr->size > 0)
286 {
287 copy->data = static_cast<BYTE*>(winpr_aligned_malloc(ptr->size, 32));
288 if (!copy)
289 {
290 free(copy);
291 return nullptr;
292 }
293 copy->size = ptr->size;
294 memcpy(copy->data, ptr->data, copy->size);
295 }
296 return &copy->pointer;
297}