FreeRDP
Loading...
Searching...
No Matches
shadow_subsystem.c
1
19#include <freerdp/config.h>
20
21#include <winpr/cmdline.h>
22
23#include <freerdp/log.h>
24
25#include "shadow.h"
26
27#include "shadow_subsystem.h"
28
29#define TAG SERVER_TAG("shadow")
30
31/* The subsystems built in, in the order they are tried */
32#define SHADOW_SUBSYSTEM_MAX 8
33#define SHADOW_SUBSYSTEM_MONITORS 16
34
35typedef struct
36{
37 char name[32];
38 pfnShadowSubsystemEntry entry;
39} SHADOW_SUBSYSTEM_CANDIDATE;
40
41static SHADOW_SUBSYSTEM_CANDIDATE g_Candidates[SHADOW_SUBSYSTEM_MAX] = WINPR_C_ARRAY_INIT;
42static size_t g_CandidateCount = 0;
43
44/* /subsystem: indexes into g_Candidates; empty means all of them, in their order */
45static size_t g_Order[SHADOW_SUBSYSTEM_MAX] = WINPR_C_ARRAY_INIT;
46static size_t g_OrderCount = 0;
47
48/* The subsystem in use: set by shadow_subsystem_set_entry or picked from the candidates */
49static pfnShadowSubsystemEntry pSubsystemEntry = nullptr;
50
51void shadow_subsystem_set_entry(pfnShadowSubsystemEntry pEntry)
52{
53 pSubsystemEntry = pEntry;
54}
55
56BOOL shadow_subsystem_add_entry(const char* name, pfnShadowSubsystemEntry pEntry)
57{
58 if (!name || !pEntry || (g_CandidateCount >= SHADOW_SUBSYSTEM_MAX))
59 return FALSE;
60
61 SHADOW_SUBSYSTEM_CANDIDATE* candidate = &g_Candidates[g_CandidateCount];
62 const int rc = _snprintf(candidate->name, sizeof(candidate->name), "%s", name);
63 if ((rc < 0) || ((size_t)rc >= sizeof(candidate->name)))
64 return FALSE;
65
66 candidate->entry = pEntry;
67 g_CandidateCount++;
68 return TRUE;
69}
70
71WINPR_ATTR_NODISCARD
72static size_t shadow_subsystem_order_count(void)
73{
74 return (g_OrderCount > 0) ? g_OrderCount : g_CandidateCount;
75}
76
77WINPR_ATTR_NODISCARD
78static const SHADOW_SUBSYSTEM_CANDIDATE* shadow_subsystem_order_at(size_t index)
79{
80 WINPR_ASSERT(index < shadow_subsystem_order_count());
81 const size_t candidate = (g_OrderCount > 0) ? g_Order[index] : index;
82 WINPR_ASSERT(candidate < g_CandidateCount);
83 return &g_Candidates[candidate];
84}
85
86/* The number of monitors a subsystem finds: 0 when it cannot run here */
87WINPR_ATTR_NODISCARD
88static UINT32 shadow_subsystem_probe(const SHADOW_SUBSYSTEM_CANDIDATE* candidate)
89{
90 WINPR_ASSERT(candidate);
91 WINPR_ASSERT(candidate->entry);
92
93 RDP_SHADOW_ENTRY_POINTS ep = WINPR_C_ARRAY_INIT;
94 if ((candidate->entry(&ep) < 0) || !ep.EnumMonitors)
95 return 0;
96
97 MONITOR_DEF monitors[SHADOW_SUBSYSTEM_MONITORS] = WINPR_C_ARRAY_INIT;
98 return ep.EnumMonitors(monitors, ARRAYSIZE(monitors));
99}
100
101/* Pick the first subsystem that finds a monitor. With a single one there is
102 * nothing to choose, so it is used without asking (and reports its own errors). */
103WINPR_ATTR_NODISCARD
104static BOOL shadow_subsystem_select(void)
105{
106 if (pSubsystemEntry)
107 return TRUE;
108
109 const size_t count = shadow_subsystem_order_count();
110 if (count == 0)
111 return FALSE;
112
113 const SHADOW_SUBSYSTEM_CANDIDATE* first = shadow_subsystem_order_at(0);
114 if (count == 1)
115 {
116 if (g_CandidateCount > 1)
117 WLog_INFO(TAG, "subsystem: %s", first->name);
118 pSubsystemEntry = first->entry;
119 return TRUE;
120 }
121
122 for (size_t index = 0; index < count; index++)
123 {
124 const SHADOW_SUBSYSTEM_CANDIDATE* candidate = shadow_subsystem_order_at(index);
125 if (shadow_subsystem_probe(candidate) > 0)
126 {
127 WLog_INFO(TAG, "subsystem: %s", candidate->name);
128 pSubsystemEntry = candidate->entry;
129 return TRUE;
130 }
131
132 WLog_INFO(TAG, "subsystem %s found no monitor%s", candidate->name,
133 (index + 1 < count) ? ", trying the next one" : "");
134 }
135
136 WLog_WARN(TAG, "no subsystem found a monitor, using %s", first->name);
137 pSubsystemEntry = first->entry;
138 return TRUE;
139}
140
141BOOL shadow_subsystem_set_order(const char* names)
142{
143 if (!names)
144 return FALSE;
145
146 if (g_CandidateCount == 0)
147 {
148 WLog_ERR(TAG, "this build has a single fixed subsystem");
149 return FALSE;
150 }
151
152 size_t count = 0;
153 char** list = CommandLineParseCommaSeparatedValues(names, &count);
154 if (!list || (count == 0))
155 {
156 free((void*)list);
157 return FALSE;
158 }
159
160 BOOL rc = TRUE;
161 size_t orderCount = 0;
162 size_t order[SHADOW_SUBSYSTEM_MAX] = WINPR_C_ARRAY_INIT;
163 for (size_t x = 0; rc && (x < count); x++)
164 {
165 size_t found = g_CandidateCount;
166 for (size_t y = 0; y < g_CandidateCount; y++)
167 {
168 if (_stricmp(list[x], g_Candidates[y].name) == 0)
169 found = y;
170 }
171
172 if (found == g_CandidateCount)
173 {
174 WLog_ERR(TAG, "unknown subsystem '%s' (/subsystem lists them)", list[x]);
175 rc = FALSE;
176 break;
177 }
178
179 BOOL duplicate = FALSE;
180 for (size_t y = 0; y < orderCount; y++)
181 {
182 if (order[y] == found)
183 duplicate = TRUE;
184 }
185
186 if (!duplicate)
187 order[orderCount++] = found;
188 }
189 free((void*)list);
190
191 if (!rc)
192 return FALSE;
193
194 memcpy(g_Order, order, sizeof(g_Order));
195 g_OrderCount = orderCount;
196 pSubsystemEntry = nullptr;
197 return TRUE;
198}
199
200void shadow_subsystem_list(void)
201{
202 const size_t count = shadow_subsystem_order_count();
203 BOOL picked = FALSE;
204
205 for (size_t index = 0; index < count; index++)
206 {
207 const SHADOW_SUBSYSTEM_CANDIDATE* candidate = shadow_subsystem_order_at(index);
208 const UINT32 monitors = shadow_subsystem_probe(candidate);
209 const BOOL pick = !picked && (monitors > 0);
210 picked |= pick;
211 WLog_INFO(TAG, " %s %s: %" PRIu32 " monitor(s)", pick ? "*" : " ", candidate->name,
212 monitors);
213 }
214
215 if (count == 0)
216 WLog_INFO(TAG, "this build has a single fixed subsystem");
217}
218
219WINPR_ATTR_NODISCARD
220static int shadow_subsystem_load_entry_points(RDP_SHADOW_ENTRY_POINTS* pEntryPoints)
221{
222 WINPR_ASSERT(pEntryPoints);
223 ZeroMemory(pEntryPoints, sizeof(RDP_SHADOW_ENTRY_POINTS));
224
225 if (!shadow_subsystem_select())
226 return -1;
227
228 if (pSubsystemEntry(pEntryPoints) < 0)
229 return -1;
230
231 return 1;
232}
233
234rdpShadowSubsystem* shadow_subsystem_new(void)
235{
236 RDP_SHADOW_ENTRY_POINTS ep = WINPR_C_ARRAY_INIT;
237
238 if (shadow_subsystem_load_entry_points(&ep) < 0)
239 return nullptr;
240
241 if (!ep.New)
242 return nullptr;
243
244 rdpShadowSubsystem* subsystem = ep.New();
245
246 if (!subsystem)
247 return nullptr;
248
249 subsystem->ep = ep;
250
251 return subsystem;
252}
253
254void shadow_subsystem_free(rdpShadowSubsystem* subsystem)
255{
256 if (subsystem && subsystem->ep.Free)
257 subsystem->ep.Free(subsystem);
258}
259
260int shadow_subsystem_init(rdpShadowSubsystem* subsystem, rdpShadowServer* server)
261{
262 int status = -1;
263
264 if (!subsystem || !subsystem->ep.Init)
265 return -1;
266
267 subsystem->server = server;
268 subsystem->selectedMonitor = server->selectedMonitor;
269
270 if (!(subsystem->MsgPipe = MessagePipe_New()))
271 goto fail;
272
273 if (!(subsystem->updateEvent = shadow_multiclient_new()))
274 goto fail;
275
276 status = subsystem->ep.Init(subsystem);
277 if (status >= 0)
278 return status;
279
280fail:
281 if (subsystem->MsgPipe)
282 {
283 MessagePipe_Free(subsystem->MsgPipe);
284 subsystem->MsgPipe = nullptr;
285 }
286
287 if (subsystem->updateEvent)
288 {
289 shadow_multiclient_free(subsystem->updateEvent);
290 subsystem->updateEvent = nullptr;
291 }
292
293 return status;
294}
295
296static void shadow_subsystem_free_queued_message(void* obj)
297{
298 wMessage* message = (wMessage*)obj;
299 if (message->Free)
300 {
301 message->Free(message);
302 message->Free = nullptr;
303 }
304}
305
306void shadow_subsystem_uninit(rdpShadowSubsystem* subsystem)
307{
308 if (!subsystem)
309 return;
310
311 if (subsystem->ep.Uninit)
312 subsystem->ep.Uninit(subsystem);
313
314 if (subsystem->MsgPipe)
315 {
316 wObject* obj1 = nullptr;
317 wObject* obj2 = nullptr;
318 /* Release resource in messages before free */
319 obj1 = MessageQueue_Object(subsystem->MsgPipe->In);
320
321 obj1->fnObjectFree = shadow_subsystem_free_queued_message;
322 MessageQueue_Clear(subsystem->MsgPipe->In);
323
324 obj2 = MessageQueue_Object(subsystem->MsgPipe->Out);
325 obj2->fnObjectFree = shadow_subsystem_free_queued_message;
326 MessageQueue_Clear(subsystem->MsgPipe->Out);
327 MessagePipe_Free(subsystem->MsgPipe);
328 subsystem->MsgPipe = nullptr;
329 }
330
331 if (subsystem->updateEvent)
332 {
333 shadow_multiclient_free(subsystem->updateEvent);
334 subsystem->updateEvent = nullptr;
335 }
336}
337
338int shadow_subsystem_start(rdpShadowSubsystem* subsystem)
339{
340 int status = 0;
341
342 if (!subsystem || !subsystem->ep.Start)
343 return -1;
344
345 status = subsystem->ep.Start(subsystem);
346
347 return status;
348}
349
350int shadow_subsystem_stop(rdpShadowSubsystem* subsystem)
351{
352 int status = 0;
353
354 if (!subsystem || !subsystem->ep.Stop)
355 return -1;
356
357 status = subsystem->ep.Stop(subsystem);
358
359 return status;
360}
361
362UINT32 shadow_enum_monitors(MONITOR_DEF* monitors, UINT32 maxMonitors)
363{
364 UINT32 numMonitors = 0;
365 RDP_SHADOW_ENTRY_POINTS ep;
366
367 if (shadow_subsystem_load_entry_points(&ep) < 0)
368 return 0;
369
370 numMonitors = ep.EnumMonitors(monitors, maxMonitors);
371
372 return numMonitors;
373}
374
381#if !defined(WITHOUT_FREERDP_3x_DEPRECATED)
382int shadow_subsystem_pointer_convert_alpha_pointer_data(
383 const BYTE* WINPR_RESTRICT pixels, BOOL premultiplied, UINT32 width, UINT32 height,
384 SHADOW_MSG_OUT_POINTER_ALPHA_UPDATE* WINPR_RESTRICT pointerColor)
385{
386 return shadow_subsystem_pointer_convert_alpha_pointer_data_to_format(
387 pixels, PIXEL_FORMAT_BGRX32, premultiplied, width, height, pointerColor);
388}
389#endif
390
391int shadow_subsystem_pointer_convert_alpha_pointer_data_to_format(
392 const BYTE* WINPR_RESTRICT pixels, UINT32 format, BOOL premultiplied, UINT32 width,
393 UINT32 height, SHADOW_MSG_OUT_POINTER_ALPHA_UPDATE* WINPR_RESTRICT pointerColor)
394{
395 UINT32 xorStep = 0;
396 UINT32 andStep = 0;
397 UINT32 andBit = 0;
398 BYTE* andBits = nullptr;
399 UINT32 andPixel = 0;
400 const size_t bpp = FreeRDPGetBytesPerPixel(format);
401
402 xorStep = (width * 3);
403 xorStep += (xorStep % 2);
404
405 andStep = ((width + 7) / 8);
406 andStep += (andStep % 2);
407
408 WINPR_ASSERT(pointerColor);
409
410 pointerColor->lengthXorMask = height * xorStep;
411 pointerColor->xorMaskData = (BYTE*)calloc(1, pointerColor->lengthXorMask);
412
413 if (!pointerColor->xorMaskData)
414 return -1;
415
416 pointerColor->lengthAndMask = height * andStep;
417 pointerColor->andMaskData = (BYTE*)calloc(1, pointerColor->lengthAndMask);
418
419 if (!pointerColor->andMaskData)
420 {
421 free(pointerColor->xorMaskData);
422 pointerColor->xorMaskData = nullptr;
423 return -1;
424 }
425
426 for (size_t y = 0; y < height; y++)
427 {
428 WINPR_ASSERT(pixels);
429 const BYTE* pSrc8 = &pixels[(width * bpp) * (height - 1 - y)];
430 BYTE* pDst8 = &(pointerColor->xorMaskData[y * xorStep]);
431
432 andBit = 0x80;
433 andBits = &(pointerColor->andMaskData[andStep * y]);
434
435 for (size_t x = 0; x < width; x++)
436 {
437 BYTE B = 0;
438 BYTE G = 0;
439 BYTE R = 0;
440 BYTE A = 0;
441
442 const UINT32 color = FreeRDPReadColor(&pSrc8[x * bpp], format);
443 FreeRDPSplitColor(color, format, &R, &G, &B, &A, nullptr);
444
445 andPixel = 0;
446
447 if (A < 64)
448 A = 0; /* pixel cannot be partially transparent */
449
450 if (!A)
451 {
452 /* transparent pixel: XOR = black, AND = 1 */
453 andPixel = 1;
454 B = G = R = 0;
455 }
456 else
457 {
458 if (premultiplied)
459 {
460 B = (B * 0xFF) / A;
461 G = (G * 0xFF) / A;
462 R = (R * 0xFF) / A;
463 }
464 }
465
466 *pDst8++ = B;
467 *pDst8++ = G;
468 *pDst8++ = R;
469
470 if (andPixel)
471 *andBits |= andBit;
472 if (!(andBit >>= 1))
473 {
474 andBits++;
475 andBit = 0x80;
476 }
477 }
478 }
479
480 return 1;
481}
482
483void shadow_subsystem_frame_update(rdpShadowSubsystem* subsystem)
484{
485 shadow_multiclient_publish_and_wait(subsystem->updateEvent);
486}
This struct contains function pointer to initialize/free objects.
Definition collections.h:52
OBJECT_FREE_FN fnObjectFree
Definition collections.h:59