FreeRDP
tsmf_X11.c
1 /*
2  * FreeRDP: A Remote Desktop Protocol Implementation
3  * Video Redirection Virtual Channel - GStreamer Decoder X11 specifics
4  *
5  * (C) Copyright 2014 Thincast Technologies GmbH
6  * (C) Copyright 2014 Armin Novak <armin.novak@thincast.com>
7  *
8  * Licensed under the Apache License, Version 2.0 (the "License");
9  * you may not use this file except in compliance with the License.
10  * You may obtain a copy of the License at
11  *
12  * http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  */
20 
21 #include <sys/types.h>
22 #include <sys/mman.h>
23 #include <sys/stat.h>
24 #ifndef __CYGWIN__
25 #include <sys/syscall.h>
26 #endif
27 
28 #include <unistd.h>
29 #include <fcntl.h>
30 #include <string.h>
31 #include <err.h>
32 #include <errno.h>
33 #include <winpr/thread.h>
34 #include <winpr/string.h>
35 #include <winpr/platform.h>
36 
37 #include <gst/gst.h>
38 
39 #if GST_VERSION_MAJOR > 0
40 #include <gst/video/videooverlay.h>
41 #else
42 #include <gst/interfaces/xoverlay.h>
43 #endif
44 
45 #include <X11/Xlib.h>
46 #include <X11/extensions/Xrandr.h>
47 #include <X11/extensions/shape.h>
48 
49 #include <freerdp/channels/tsmf.h>
50 
51 #include "tsmf_platform.h"
52 #include "tsmf_constants.h"
53 #include "tsmf_decoder.h"
54 
55 #if !defined(WITH_XEXT)
56 #warning "Building TSMF without shape extension support"
57 #endif
58 
59 struct X11Handle
60 {
61  int shmid;
62  int* xfwin;
63 #if defined(WITH_XEXT)
64  BOOL has_shape;
65 #endif
66  Display* disp;
67  Window subwin;
68  BOOL subwinMapped;
69 #if GST_VERSION_MAJOR > 0
70  GstVideoOverlay* overlay;
71 #else
72  GstXOverlay* overlay;
73 #endif
74  int subwinWidth;
75  int subwinHeight;
76  int subwinX;
77  int subwinY;
78 };
79 
80 static const char* get_shm_id()
81 {
82  static char shm_id[128];
83  sprintf_s(shm_id, sizeof(shm_id), "/com.freerdp.xfreerdp.tsmf_%016X", GetCurrentProcessId());
84  return shm_id;
85 }
86 
87 static GstBusSyncReply tsmf_platform_bus_sync_handler(GstBus* bus, GstMessage* message,
88  gpointer user_data)
89 {
90  struct X11Handle* hdl;
91 
92  TSMFGstreamerDecoder* decoder = user_data;
93 
94  if (GST_MESSAGE_TYPE(message) != GST_MESSAGE_ELEMENT)
95  return GST_BUS_PASS;
96 
97 #if GST_VERSION_MAJOR > 0
98  if (!gst_is_video_overlay_prepare_window_handle_message(message))
99  return GST_BUS_PASS;
100 #else
101  if (!gst_structure_has_name(message->structure, "prepare-xwindow-id"))
102  return GST_BUS_PASS;
103 #endif
104 
105  hdl = (struct X11Handle*)decoder->platform;
106 
107  if (hdl->subwin)
108  {
109 #if GST_VERSION_MAJOR > 0
110  hdl->overlay = GST_VIDEO_OVERLAY(GST_MESSAGE_SRC(message));
111  gst_video_overlay_set_window_handle(hdl->overlay, hdl->subwin);
112  gst_video_overlay_handle_events(hdl->overlay, FALSE);
113 #else
114  hdl->overlay = GST_X_OVERLAY(GST_MESSAGE_SRC(message));
115 #if GST_CHECK_VERSION(0, 10, 31)
116  gst_x_overlay_set_window_handle(hdl->overlay, hdl->subwin);
117 #else
118  gst_x_overlay_set_xwindow_id(hdl->overlay, hdl->subwin);
119 #endif
120  gst_x_overlay_handle_events(hdl->overlay, TRUE);
121 #endif
122 
123  if (hdl->subwinWidth != -1 && hdl->subwinHeight != -1 && hdl->subwinX != -1 &&
124  hdl->subwinY != -1)
125  {
126 #if GST_VERSION_MAJOR > 0
127  if (!gst_video_overlay_set_render_rectangle(hdl->overlay, 0, 0, hdl->subwinWidth,
128  hdl->subwinHeight))
129  {
130  WLog_ERR(TAG, "Could not resize overlay!");
131  }
132 
133  gst_video_overlay_expose(hdl->overlay);
134 #else
135  if (!gst_x_overlay_set_render_rectangle(hdl->overlay, 0, 0, hdl->subwinWidth,
136  hdl->subwinHeight))
137  {
138  WLog_ERR(TAG, "Could not resize overlay!");
139  }
140 
141  gst_x_overlay_expose(hdl->overlay);
142 #endif
143  XLockDisplay(hdl->disp);
144  XMoveResizeWindow(hdl->disp, hdl->subwin, hdl->subwinX, hdl->subwinY, hdl->subwinWidth,
145  hdl->subwinHeight);
146  XSync(hdl->disp, FALSE);
147  XUnlockDisplay(hdl->disp);
148  }
149  }
150  else
151  {
152  g_warning("Window was not available before retrieving the overlay!");
153  }
154 
155  gst_message_unref(message);
156 
157  return GST_BUS_DROP;
158 }
159 
160 const char* tsmf_platform_get_video_sink(void)
161 {
162  return "autovideosink";
163 }
164 
165 const char* tsmf_platform_get_audio_sink(void)
166 {
167  return "autoaudiosink";
168 }
169 
170 int tsmf_platform_create(TSMFGstreamerDecoder* decoder)
171 {
172  struct X11Handle* hdl;
173 
174  if (!decoder)
175  return -1;
176 
177  if (decoder->platform)
178  return -1;
179 
180  hdl = calloc(1, sizeof(struct X11Handle));
181  if (!hdl)
182  {
183  WLog_ERR(TAG, "Could not allocate handle.");
184  return -1;
185  }
186 
187  decoder->platform = hdl;
188  hdl->shmid = shm_open(get_shm_id(), (O_RDWR | O_CREAT), (PROT_READ | PROT_WRITE));
189  if (hdl->shmid == -1)
190  {
191  char ebuffer[256] = { 0 };
192  WLog_ERR(TAG, "failed to get access to shared memory - shmget(%s): %i - %s", get_shm_id(),
193  errno, winpr_strerror(errno, ebuffer, sizeof(ebuffer)));
194  return -2;
195  }
196 
197  hdl->xfwin = mmap(0, sizeof(void*), PROT_READ | PROT_WRITE, MAP_SHARED, hdl->shmid, 0);
198  if (hdl->xfwin == MAP_FAILED)
199  {
200  WLog_ERR(TAG, "shmat failed!");
201  return -3;
202  }
203 
204  hdl->disp = XOpenDisplay(NULL);
205  if (!hdl->disp)
206  {
207  WLog_ERR(TAG, "Failed to open display");
208  return -4;
209  }
210 
211  hdl->subwinMapped = FALSE;
212  hdl->subwinX = -1;
213  hdl->subwinY = -1;
214  hdl->subwinWidth = -1;
215  hdl->subwinHeight = -1;
216 
217  return 0;
218 }
219 
220 int tsmf_platform_set_format(TSMFGstreamerDecoder* decoder)
221 {
222  if (!decoder)
223  return -1;
224 
225  if (decoder->media_type == TSMF_MAJOR_TYPE_VIDEO)
226  {
227  }
228 
229  return 0;
230 }
231 
232 int tsmf_platform_register_handler(TSMFGstreamerDecoder* decoder)
233 {
234  GstBus* bus;
235 
236  if (!decoder)
237  return -1;
238 
239  if (!decoder->pipe)
240  return -1;
241 
242  bus = gst_pipeline_get_bus(GST_PIPELINE(decoder->pipe));
243 
244 #if GST_VERSION_MAJOR > 0
245  gst_bus_set_sync_handler(bus, (GstBusSyncHandler)tsmf_platform_bus_sync_handler, decoder, NULL);
246 #else
247  gst_bus_set_sync_handler(bus, (GstBusSyncHandler)tsmf_platform_bus_sync_handler, decoder);
248 #endif
249 
250  if (!bus)
251  {
252  WLog_ERR(TAG, "gst_pipeline_get_bus failed!");
253  return 1;
254  }
255 
256  gst_object_unref(bus);
257 
258  return 0;
259 }
260 
261 int tsmf_platform_free(TSMFGstreamerDecoder* decoder)
262 {
263  struct X11Handle* hdl = decoder->platform;
264 
265  if (!hdl)
266  return -1;
267 
268  if (hdl->disp)
269  XCloseDisplay(hdl->disp);
270 
271  if (hdl->xfwin)
272  munmap(0, sizeof(void*));
273 
274  if (hdl->shmid >= 0)
275  close(hdl->shmid);
276 
277  free(hdl);
278  decoder->platform = NULL;
279 
280  return 0;
281 }
282 
283 int tsmf_window_create(TSMFGstreamerDecoder* decoder)
284 {
285  struct X11Handle* hdl;
286 
287  if (decoder->media_type != TSMF_MAJOR_TYPE_VIDEO)
288  {
289  decoder->ready = TRUE;
290  return -3;
291  }
292  else
293  {
294  if (!decoder)
295  return -1;
296 
297  if (!decoder->platform)
298  return -1;
299 
300  hdl = (struct X11Handle*)decoder->platform;
301 
302  if (!hdl->subwin)
303  {
304  XLockDisplay(hdl->disp);
305  hdl->subwin = XCreateSimpleWindow(hdl->disp, *(int*)hdl->xfwin, 0, 0, 1, 1, 0, 0, 0);
306  XUnlockDisplay(hdl->disp);
307 
308  if (!hdl->subwin)
309  {
310  WLog_ERR(TAG, "Could not create subwindow!");
311  }
312  }
313 
314  tsmf_window_map(decoder);
315 
316  decoder->ready = TRUE;
317 #if defined(WITH_XEXT)
318  int event, error;
319  XLockDisplay(hdl->disp);
320  hdl->has_shape = XShapeQueryExtension(hdl->disp, &event, &error);
321  XUnlockDisplay(hdl->disp);
322 #endif
323  }
324 
325  return 0;
326 }
327 
328 int tsmf_window_resize(TSMFGstreamerDecoder* decoder, int x, int y, int width, int height,
329  int nr_rects, RDP_RECT* rects)
330 {
331  struct X11Handle* hdl;
332 
333  if (!decoder)
334  return -1;
335 
336  if (decoder->media_type != TSMF_MAJOR_TYPE_VIDEO)
337  {
338  return -3;
339  }
340 
341  if (!decoder->platform)
342  return -1;
343 
344  hdl = (struct X11Handle*)decoder->platform;
345  DEBUG_TSMF("resize: x=%d, y=%d, w=%d, h=%d", x, y, width, height);
346 
347  if (hdl->overlay)
348  {
349 #if GST_VERSION_MAJOR > 0
350 
351  if (!gst_video_overlay_set_render_rectangle(hdl->overlay, 0, 0, width, height))
352  {
353  WLog_ERR(TAG, "Could not resize overlay!");
354  }
355 
356  gst_video_overlay_expose(hdl->overlay);
357 #else
358  if (!gst_x_overlay_set_render_rectangle(hdl->overlay, 0, 0, width, height))
359  {
360  WLog_ERR(TAG, "Could not resize overlay!");
361  }
362 
363  gst_x_overlay_expose(hdl->overlay);
364 #endif
365  }
366 
367  if (hdl->subwin)
368  {
369  hdl->subwinX = x;
370  hdl->subwinY = y;
371  hdl->subwinWidth = width;
372  hdl->subwinHeight = height;
373 
374  XLockDisplay(hdl->disp);
375  XMoveResizeWindow(hdl->disp, hdl->subwin, hdl->subwinX, hdl->subwinY, hdl->subwinWidth,
376  hdl->subwinHeight);
377 
378  /* Unmap the window if there are no visibility rects */
379  if (nr_rects == 0)
380  tsmf_window_unmap(decoder);
381  else
382  tsmf_window_map(decoder);
383 
384 #if defined(WITH_XEXT)
385  if (hdl->has_shape)
386  {
387  XRectangle* xrects = NULL;
388 
389  if (nr_rects == 0)
390  {
391  xrects = calloc(1, sizeof(XRectangle));
392  xrects->x = x;
393  xrects->y = y;
394  xrects->width = width;
395  xrects->height = height;
396  }
397  else
398  {
399  xrects = calloc(nr_rects, sizeof(XRectangle));
400  }
401 
402  if (xrects)
403  {
404  for (int i = 0; i < nr_rects; i++)
405  {
406  xrects[i].x = rects[i].x - x;
407  xrects[i].y = rects[i].y - y;
408  xrects[i].width = rects[i].width;
409  xrects[i].height = rects[i].height;
410  }
411 
412  XShapeCombineRectangles(hdl->disp, hdl->subwin, ShapeBounding, x, y, xrects,
413  nr_rects, ShapeSet, 0);
414  free(xrects);
415  }
416  }
417 #endif
418  XSync(hdl->disp, FALSE);
419  XUnlockDisplay(hdl->disp);
420  }
421 
422  return 0;
423 }
424 
425 int tsmf_window_map(TSMFGstreamerDecoder* decoder)
426 {
427  struct X11Handle* hdl;
428  if (!decoder)
429  return -1;
430 
431  hdl = (struct X11Handle*)decoder->platform;
432 
433  /* Only need to map the window if it is not currently mapped */
434  if ((hdl->subwin) && (!hdl->subwinMapped))
435  {
436  XLockDisplay(hdl->disp);
437  XMapWindow(hdl->disp, hdl->subwin);
438  hdl->subwinMapped = TRUE;
439  XSync(hdl->disp, FALSE);
440  XUnlockDisplay(hdl->disp);
441  }
442 
443  return 0;
444 }
445 
446 int tsmf_window_unmap(TSMFGstreamerDecoder* decoder)
447 {
448  struct X11Handle* hdl;
449  if (!decoder)
450  return -1;
451 
452  hdl = (struct X11Handle*)decoder->platform;
453 
454  /* only need to unmap window if it is currently mapped */
455  if ((hdl->subwin) && (hdl->subwinMapped))
456  {
457  XLockDisplay(hdl->disp);
458  XUnmapWindow(hdl->disp, hdl->subwin);
459  hdl->subwinMapped = FALSE;
460  XSync(hdl->disp, FALSE);
461  XUnlockDisplay(hdl->disp);
462  }
463 
464  return 0;
465 }
466 
467 int tsmf_window_destroy(TSMFGstreamerDecoder* decoder)
468 {
469  struct X11Handle* hdl;
470 
471  if (!decoder)
472  return -1;
473 
474  decoder->ready = FALSE;
475 
476  if (decoder->media_type != TSMF_MAJOR_TYPE_VIDEO)
477  return -3;
478 
479  if (!decoder->platform)
480  return -1;
481 
482  hdl = (struct X11Handle*)decoder->platform;
483 
484  if (hdl->subwin)
485  {
486  XLockDisplay(hdl->disp);
487  XDestroyWindow(hdl->disp, hdl->subwin);
488  XSync(hdl->disp, FALSE);
489  XUnlockDisplay(hdl->disp);
490  }
491 
492  hdl->overlay = NULL;
493  hdl->subwin = 0;
494  hdl->subwinMapped = FALSE;
495  hdl->subwinX = -1;
496  hdl->subwinY = -1;
497  hdl->subwinWidth = -1;
498  hdl->subwinHeight = -1;
499  return 0;
500 }