FreeRDP
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 
37 static 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;
130 fail:
131  region16_clear(&surface->gdi.invalidRegion);
132  XSetClipMask(xfc->display, xfc->gc, None);
133  XSync(xfc->display, False);
134  return rc;
135 }
136 
137 static 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 
144 static 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 
190 UINT 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 
253 fail:
254  return status;
255 }
256 
257 static 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 
281 static 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;
390 error_set_surface_data:
391  surface->image->data = NULL;
392  XDestroyImage(surface->image);
393 error_surface_image:
394  winpr_aligned_free(surface->stage);
395 out_free_gdidata:
396  winpr_aligned_free(surface->gdi.data);
397 out_free:
398  free(surface);
399  return ret;
400 }
401 
407 static 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 
442 static 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 
460 void 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 
483 void 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.