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