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