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