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