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