FreeRDP
Loading...
Searching...
No Matches
xf_gfx.c
1
22#include <freerdp/config.h>
23
24#include <math.h>
25
26#include <winpr/assert.h>
27#include <winpr/cast.h>
28
29#include <freerdp/log.h>
30#include "xf_gfx.h"
31#include "xf_rail.h"
32#include "xf_utils.h"
33#include "xf_window.h"
34
35#include <X11/Xutil.h>
36
37#define TAG CLIENT_TAG("x11")
38
39static UINT xf_OutputUpdate(xfContext* xfc, xfGfxSurface* surface)
40{
41 UINT rc = ERROR_INTERNAL_ERROR;
42 UINT32 surfaceX = 0;
43 UINT32 surfaceY = 0;
44 RECTANGLE_16 surfaceRect = WINPR_C_ARRAY_INIT;
45 UINT32 nbRects = 0;
46 const RECTANGLE_16* rects = nullptr;
47
48 WINPR_ASSERT(xfc);
49 WINPR_ASSERT(surface);
50
51 rdpGdi* gdi = xfc->common.context.gdi;
52 WINPR_ASSERT(gdi);
53
54 rdpSettings* settings = xfc->common.context.settings;
55 WINPR_ASSERT(settings);
56
57 surfaceX = surface->gdi.outputOriginX;
58 surfaceY = surface->gdi.outputOriginY;
59 surfaceRect.left = 0;
60 surfaceRect.top = 0;
61 surfaceRect.right = WINPR_ASSERTING_INT_CAST(UINT16, surface->gdi.mappedWidth);
62 surfaceRect.bottom = WINPR_ASSERTING_INT_CAST(UINT16, surface->gdi.mappedHeight);
63 LogDynAndXSetClipMask(xfc->log, xfc->display, xfc->gc, None);
64 LogDynAndXSetFunction(xfc->log, xfc->display, xfc->gc, GXcopy);
65 LogDynAndXSetFillStyle(xfc->log, xfc->display, xfc->gc, FillSolid);
66 if (!region16_intersect_rect(&(surface->gdi.invalidRegion), &(surface->gdi.invalidRegion),
67 &surfaceRect))
68 return ERROR_INTERNAL_ERROR;
69
70 WINPR_ASSERT(surface->gdi.mappedWidth);
71 WINPR_ASSERT(surface->gdi.mappedHeight);
72 const double sx = 1.0 * surface->gdi.outputTargetWidth / (double)surface->gdi.mappedWidth;
73 const double sy = 1.0 * surface->gdi.outputTargetHeight / (double)surface->gdi.mappedHeight;
74
75 if (!(rects = region16_rects(&surface->gdi.invalidRegion, &nbRects)))
76 return CHANNEL_RC_OK;
77
78 xf_lock_x11(xfc);
79 for (UINT32 x = 0; x < nbRects; x++)
80 {
81 const RECTANGLE_16* rect = &rects[x];
82 const UINT32 nXSrc = rect->left;
83 const UINT32 nYSrc = rect->top;
84 const UINT32 swidth = rect->right - nXSrc;
85 const UINT32 sheight = rect->bottom - nYSrc;
86 const UINT32 nXDst = (UINT32)lround(1.0 * surfaceX + nXSrc * sx);
87 const UINT32 nYDst = (UINT32)lround(1.0 * surfaceY + nYSrc * sy);
88 const UINT32 dwidth = (UINT32)lround(1.0 * swidth * sx);
89 const UINT32 dheight = (UINT32)lround(1.0 * sheight * sy);
90
91 if (surface->stage)
92 {
93 if (!freerdp_image_scale(surface->stage, gdi->dstFormat, surface->stageScanline, nXSrc,
94 nYSrc, dwidth, dheight, surface->gdi.data, surface->gdi.format,
95 surface->gdi.scanline, nXSrc, nYSrc, swidth, sheight))
96 goto fail;
97 }
98
99 if (xfc->remote_app)
100 {
101 LogDynAndXPutImage(xfc->log, xfc->display, xfc->primary, xfc->gc, surface->image,
102 WINPR_ASSERTING_INT_CAST(int, nXSrc),
103 WINPR_ASSERTING_INT_CAST(int, nYSrc),
104 WINPR_ASSERTING_INT_CAST(int, nXDst),
105 WINPR_ASSERTING_INT_CAST(int, nYDst), dwidth, dheight);
106 (void)xf_rail_paint_surface(xfc, surface->gdi.windowId, rect);
107 }
108 else
109#ifdef WITH_XRENDER
110 if (freerdp_settings_get_bool(settings, FreeRDP_SmartSizing) ||
111 freerdp_settings_get_bool(settings, FreeRDP_MultiTouchGestures))
112 {
113 LogDynAndXPutImage(xfc->log, xfc->display, xfc->primary, xfc->gc, surface->image,
114 WINPR_ASSERTING_INT_CAST(int, nXSrc),
115 WINPR_ASSERTING_INT_CAST(int, nYSrc),
116 WINPR_ASSERTING_INT_CAST(int, nXDst),
117 WINPR_ASSERTING_INT_CAST(int, nYDst), dwidth, dheight);
118 xf_draw_screen(xfc, WINPR_ASSERTING_INT_CAST(int32_t, nXDst),
119 WINPR_ASSERTING_INT_CAST(int32_t, nYDst),
120 WINPR_ASSERTING_INT_CAST(int32_t, dwidth),
121 WINPR_ASSERTING_INT_CAST(int32_t, dheight));
122 }
123 else
124#endif
125 {
126 LogDynAndXPutImage(xfc->log, xfc->display, xfc->drawable, xfc->gc, surface->image,
127 WINPR_ASSERTING_INT_CAST(int, nXSrc),
128 WINPR_ASSERTING_INT_CAST(int, nYSrc),
129 WINPR_ASSERTING_INT_CAST(int, nXDst),
130 WINPR_ASSERTING_INT_CAST(int, nYDst), dwidth, dheight);
131 }
132 }
133
134 rc = CHANNEL_RC_OK;
135fail:
136 region16_clear(&surface->gdi.invalidRegion);
137 LogDynAndXSetClipMask(xfc->log, xfc->display, xfc->gc, None);
138 LogDynAndXSync(xfc->log, xfc->display, False);
139 xf_unlock_x11(xfc);
140 return rc;
141}
142
143static UINT xf_WindowUpdate(RdpgfxClientContext* context, xfGfxSurface* surface)
144{
145 WINPR_ASSERT(context);
146 WINPR_ASSERT(surface);
147 return IFCALLRESULT(CHANNEL_RC_OK, context->UpdateWindowFromSurface, context, &surface->gdi);
148}
149
150static UINT xf_UpdateSurfaces(RdpgfxClientContext* context)
151{
152 UINT16 count = 0;
153 UINT status = CHANNEL_RC_OK;
154 UINT16* pSurfaceIds = nullptr;
155 rdpGdi* gdi = (rdpGdi*)context->custom;
156 xfContext* xfc = nullptr;
157
158 if (!gdi)
159 return status;
160
161 if (gdi->suppressOutput)
162 return CHANNEL_RC_OK;
163
164 xfc = (xfContext*)gdi->context;
165 EnterCriticalSection(&context->mux);
166 status = context->GetSurfaceIds(context, &pSurfaceIds, &count);
167 if (status != CHANNEL_RC_OK)
168 goto fail;
169
170 for (UINT32 index = 0; index < count; index++)
171 {
172 xfGfxSurface* surface = (xfGfxSurface*)context->GetSurfaceData(context, pSurfaceIds[index]);
173
174 if (!surface)
175 continue;
176
177 /* If UpdateSurfaceArea callback is available, the output has already been updated. */
178 if (context->UpdateSurfaceArea)
179 {
180 if (surface->gdi.handleInUpdateSurfaceArea)
181 continue;
182 }
183
184 if (surface->gdi.outputMapped)
185 status = xf_OutputUpdate(xfc, surface);
186 else if (surface->gdi.windowMapped)
187 status = xf_WindowUpdate(context, surface);
188
189 if (status != CHANNEL_RC_OK)
190 break;
191 }
192
193fail:
194 free(pSurfaceIds);
195 LeaveCriticalSection(&context->mux);
196 return status;
197}
198
199UINT xf_OutputExpose(xfContext* xfc, UINT32 x, UINT32 y, UINT32 width, UINT32 height)
200{
201 UINT16 count = 0;
202 UINT status = ERROR_INTERNAL_ERROR;
203 RECTANGLE_16 invalidRect = WINPR_C_ARRAY_INIT;
204 RECTANGLE_16 intersection = WINPR_C_ARRAY_INIT;
205 UINT16* pSurfaceIds = nullptr;
206 RdpgfxClientContext* context = nullptr;
207
208 WINPR_ASSERT(xfc);
209 WINPR_ASSERT(xfc->common.context.gdi);
210
211 context = xfc->common.context.gdi->gfx;
212 WINPR_ASSERT(context);
213
214 invalidRect.left = WINPR_ASSERTING_INT_CAST(UINT16, x);
215 invalidRect.top = WINPR_ASSERTING_INT_CAST(UINT16, y);
216 invalidRect.right = WINPR_ASSERTING_INT_CAST(UINT16, x + width);
217 invalidRect.bottom = WINPR_ASSERTING_INT_CAST(UINT16, y + height);
218 status = context->GetSurfaceIds(context, &pSurfaceIds, &count);
219
220 if (status != CHANNEL_RC_OK)
221 goto fail;
222
223 if (!TryEnterCriticalSection(&context->mux))
224 {
225 free(pSurfaceIds);
226 return CHANNEL_RC_OK;
227 }
228 for (UINT32 index = 0; index < count; index++)
229 {
230 RECTANGLE_16 surfaceRect = WINPR_C_ARRAY_INIT;
231 xfGfxSurface* surface = (xfGfxSurface*)context->GetSurfaceData(context, pSurfaceIds[index]);
232
233 if (!surface || (!surface->gdi.outputMapped && !surface->gdi.windowMapped))
234 continue;
235
236 surfaceRect.left = WINPR_ASSERTING_INT_CAST(UINT16, surface->gdi.outputOriginX);
237 surfaceRect.top = WINPR_ASSERTING_INT_CAST(UINT16, surface->gdi.outputOriginY);
238 surfaceRect.right = WINPR_ASSERTING_INT_CAST(UINT16, surface->gdi.outputOriginX +
239 surface->gdi.outputTargetWidth);
240 surfaceRect.bottom = WINPR_ASSERTING_INT_CAST(UINT16, surface->gdi.outputOriginY +
241 surface->gdi.outputTargetHeight);
242
243 if (rectangles_intersection(&invalidRect, &surfaceRect, &intersection))
244 {
245 /* Invalid rects are specified relative to surface origin */
246 intersection.left -= surfaceRect.left;
247 intersection.top -= surfaceRect.top;
248 intersection.right -= surfaceRect.left;
249 intersection.bottom -= surfaceRect.top;
250 if (!region16_union_rect(&surface->gdi.invalidRegion, &surface->gdi.invalidRegion,
251 &intersection))
252 {
253 free(pSurfaceIds);
254 LeaveCriticalSection(&context->mux);
255
256 goto fail;
257 }
258 }
259 }
260
261 free(pSurfaceIds);
262 LeaveCriticalSection(&context->mux);
263 IFCALLRET(context->UpdateSurfaces, status, context);
264
265 if (status != CHANNEL_RC_OK)
266 goto fail;
267
268fail:
269 return status;
270}
271
272static UINT32 x11_pad_scanline(UINT32 scanline, UINT32 inPad)
273{
274 /* Ensure X11 alignment is met */
275 if (inPad > 0)
276 {
277 const UINT32 align = inPad / 8;
278 const UINT32 pad = align - scanline % align;
279
280 if (align != pad)
281 scanline += pad;
282 }
283
284 /* 16 byte alignment is required for ASM optimized code */
285 if (scanline % 16)
286 scanline += 16 - scanline % 16;
287
288 return scanline;
289}
290
291static void DestroySurface(xfGfxSurface* surface)
292{
293 if (!surface)
294 return;
295
296#ifdef WITH_GFX_H264
297 h264_context_free(surface->gdi.h264);
298#endif
299#if defined(WITH_GFX_AV1)
300 freerdp_av1_context_free(surface->gdi.av1);
301#endif
302 if (surface->image)
303 {
304 surface->image->data = nullptr;
305 XDestroyImage(surface->image);
306 }
307
308 winpr_aligned_free(surface->gdi.data);
309 winpr_aligned_free(surface->stage);
310 region16_uninit(&surface->gdi.invalidRegion);
311 free(surface);
312}
313
319static UINT xf_CreateSurface(RdpgfxClientContext* context,
320 const RDPGFX_CREATE_SURFACE_PDU* createSurface)
321{
322 UINT ret = CHANNEL_RC_NO_MEMORY;
323 size_t size = 0;
324 WINPR_ASSERT(context);
325 WINPR_ASSERT(createSurface);
326
327 rdpGdi* gdi = (rdpGdi*)context->custom;
328 xfContext* xfc = (xfContext*)gdi->context;
329
330 xfGfxSurface* surface = (xfGfxSurface*)calloc(1, sizeof(xfGfxSurface));
331
332 if (!surface)
333 return CHANNEL_RC_NO_MEMORY;
334
335 surface->gdi.codecs = context->codecs;
336
337 if (!surface->gdi.codecs)
338 {
339 WLog_ERR(TAG, "global GDI codecs aren't set");
340 goto fail;
341 }
342
343 surface->gdi.surfaceId = createSurface->surfaceId;
344 surface->gdi.width = x11_pad_scanline(createSurface->width, 0);
345 surface->gdi.height = x11_pad_scanline(createSurface->height, 0);
346 surface->gdi.mappedWidth = createSurface->width;
347 surface->gdi.mappedHeight = createSurface->height;
348 surface->gdi.outputTargetWidth = createSurface->width;
349 surface->gdi.outputTargetHeight = createSurface->height;
350
351 const BOOL rails =
352 freerdp_settings_get_bool(gdi->context->settings, FreeRDP_RemoteApplicationMode);
353 switch (createSurface->pixelFormat)
354 {
355 case GFX_PIXEL_FORMAT_ARGB_8888:
356 surface->gdi.format = PIXEL_FORMAT_BGRA32;
357 break;
358
359 case GFX_PIXEL_FORMAT_XRGB_8888:
360 surface->gdi.format = rails ? PIXEL_FORMAT_BGRA32 : PIXEL_FORMAT_BGRX32;
361 break;
362
363 default:
364 WLog_ERR(TAG, "unknown pixelFormat 0x%" PRIx32 "", createSurface->pixelFormat);
365 ret = ERROR_INTERNAL_ERROR;
366 goto fail;
367 }
368
369 surface->gdi.scanline = surface->gdi.width * FreeRDPGetBytesPerPixel(surface->gdi.format);
370 surface->gdi.scanline = x11_pad_scanline(surface->gdi.scanline,
371 WINPR_ASSERTING_INT_CAST(uint32_t, xfc->scanline_pad));
372 size = 1ull * surface->gdi.scanline * surface->gdi.height;
373 surface->gdi.data = (BYTE*)winpr_aligned_malloc(size, 16);
374
375 if (!surface->gdi.data)
376 {
377 WLog_ERR(TAG, "unable to allocate GDI data");
378 goto fail;
379 }
380
381 memset(surface->gdi.data, 0xff, size);
382
383 if (FreeRDPAreColorFormatsEqualNoAlpha(gdi->dstFormat, surface->gdi.format))
384 {
385 WINPR_ASSERT(xfc->depth != 0);
386 surface->image = LogDynAndXCreateImage(
387 xfc->log, xfc->display, xfc->visual, WINPR_ASSERTING_INT_CAST(uint32_t, xfc->depth),
388 ZPixmap, 0, (char*)surface->gdi.data, surface->gdi.mappedWidth,
389 surface->gdi.mappedHeight, xfc->scanline_pad,
390 WINPR_ASSERTING_INT_CAST(int, surface->gdi.scanline));
391 }
392 else
393 {
394 UINT32 width = surface->gdi.width;
395 UINT32 bytes = FreeRDPGetBytesPerPixel(gdi->dstFormat);
396 surface->stageScanline = width * bytes;
397 surface->stageScanline = x11_pad_scanline(
398 surface->stageScanline, WINPR_ASSERTING_INT_CAST(uint32_t, xfc->scanline_pad));
399 size = 1ull * surface->stageScanline * surface->gdi.height;
400 surface->stage = (BYTE*)winpr_aligned_malloc(size, 16);
401
402 if (!surface->stage)
403 {
404 WLog_ERR(TAG, "unable to allocate stage buffer");
405 goto fail;
406 }
407
408 memset(surface->stage, 0xff, size);
409 WINPR_ASSERT(xfc->depth != 0);
410 surface->image = LogDynAndXCreateImage(
411 xfc->log, xfc->display, xfc->visual, WINPR_ASSERTING_INT_CAST(uint32_t, xfc->depth),
412 ZPixmap, 0, (char*)surface->stage, surface->gdi.mappedWidth, surface->gdi.mappedHeight,
413 xfc->scanline_pad, WINPR_ASSERTING_INT_CAST(int, surface->stageScanline));
414 }
415
416 if (!surface->image)
417 {
418 WLog_ERR(TAG, "an error occurred when creating the XImage");
419 goto fail;
420 }
421
422 surface->image->byte_order = LSBFirst;
423 surface->image->bitmap_bit_order = LSBFirst;
424
425 region16_init(&surface->gdi.invalidRegion);
426
427 if (context->SetSurfaceData(context, surface->gdi.surfaceId, (void*)surface) != CHANNEL_RC_OK)
428 {
429 WLog_ERR(TAG, "an error occurred during SetSurfaceData");
430 goto fail;
431 }
432
433 return CHANNEL_RC_OK;
434fail:
435 DestroySurface(surface);
436 return ret;
437}
438
444static UINT xf_DeleteSurface(RdpgfxClientContext* context,
445 const RDPGFX_DELETE_SURFACE_PDU* deleteSurface)
446{
447 rdpCodecs* codecs = nullptr;
448
449 UINT status = 0;
450 EnterCriticalSection(&context->mux);
451 xfGfxSurface* surface =
452 (xfGfxSurface*)context->GetSurfaceData(context, deleteSurface->surfaceId);
453
454 if (surface)
455 {
456 if (surface->gdi.windowMapped)
457 {
458 status = IFCALLRESULT(CHANNEL_RC_OK, context->UnmapWindowForSurface, context,
459 surface->gdi.windowId);
460 if (status != CHANNEL_RC_OK)
461 goto fail;
462 }
463
464 codecs = surface->gdi.codecs;
465 DestroySurface(surface);
466 }
467
468 status = context->SetSurfaceData(context, deleteSurface->surfaceId, nullptr);
469
470 if (codecs && codecs->progressive)
471 progressive_delete_surface_context(codecs->progressive, deleteSurface->surfaceId);
472
473fail:
474 LeaveCriticalSection(&context->mux);
475 return status;
476}
477
478static UINT xf_UnmapWindowForSurface(RdpgfxClientContext* context, UINT64 windowID)
479{
480 WINPR_ASSERT(context);
481 rdpGdi* gdi = (rdpGdi*)context->custom;
482 WINPR_ASSERT(gdi);
483
484 xfContext* xfc = (xfContext*)gdi->context;
485 WINPR_ASSERT(gdi->context);
486
487 if (freerdp_settings_get_bool(gdi->context->settings, FreeRDP_RemoteApplicationMode))
488 {
489 xfAppWindow* appWindow = xf_rail_get_window(xfc, windowID, FALSE);
490 if (appWindow)
491 xf_AppWindowDestroyImage(appWindow);
492 xf_rail_return_window(appWindow, FALSE);
493 }
494
495 WLog_WARN(TAG, "function not implemented");
496 return CHANNEL_RC_OK;
497}
498
499static UINT xf_UpdateWindowFromSurface(RdpgfxClientContext* context, gdiGfxSurface* surface)
500{
501 WINPR_ASSERT(context);
502 WINPR_ASSERT(surface);
503
504 rdpGdi* gdi = (rdpGdi*)context->custom;
505 WINPR_ASSERT(gdi);
506
507 xfContext* xfc = (xfContext*)gdi->context;
508 WINPR_ASSERT(gdi->context);
509
510 if (freerdp_settings_get_bool(gdi->context->settings, FreeRDP_RemoteApplicationMode))
511 return xf_AppUpdateWindowFromSurface(xfc, surface);
512
513 WLog_WARN(TAG, "function not implemented");
514 return CHANNEL_RC_OK;
515}
516
517void xf_graphics_pipeline_init(xfContext* xfc, RdpgfxClientContext* gfx)
518{
519 rdpGdi* gdi = nullptr;
520 const rdpSettings* settings = nullptr;
521 WINPR_ASSERT(xfc);
522 WINPR_ASSERT(gfx);
523
524 settings = xfc->common.context.settings;
525 WINPR_ASSERT(settings);
526
527 gdi = xfc->common.context.gdi;
528
529 gdi_graphics_pipeline_init(gdi, gfx);
530
531 if (!freerdp_settings_get_bool(settings, FreeRDP_SoftwareGdi))
532 {
533 gfx->UpdateSurfaces = xf_UpdateSurfaces;
534 gfx->CreateSurface = xf_CreateSurface;
535 gfx->DeleteSurface = xf_DeleteSurface;
536 }
537
538 gfx->UpdateWindowFromSurface = xf_UpdateWindowFromSurface;
539 gfx->UnmapWindowForSurface = xf_UnmapWindowForSurface;
540}
541
542void xf_graphics_pipeline_uninit(xfContext* xfc, RdpgfxClientContext* gfx)
543{
544 WINPR_ASSERT(xfc);
545
546 rdpGdi* gdi = xfc->common.context.gdi;
547 gdi_graphics_pipeline_uninit(gdi, gfx);
548}
WINPR_ATTR_NODISCARD FREERDP_API BOOL freerdp_settings_get_bool(const rdpSettings *settings, FreeRDP_Settings_Keys_Bool id)
Returns a boolean settings value.