FreeRDP
Loading...
Searching...
No Matches
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
59struct 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
80WINPR_ATTR_NODISCARD
81static const char* get_shm_id()
82{
83 static char shm_id[128] = WINPR_C_ARRAY_INIT;
84 if (sprintf_s(shm_id, sizeof(shm_id), "/com.freerdp.xfreerdp.tsmf_%016X",
85 GetCurrentProcessId()) < 0)
86 return nullptr;
87 return shm_id;
88}
89
90static GstBusSyncReply tsmf_platform_bus_sync_handler(GstBus* bus, GstMessage* message,
91 gpointer user_data)
92{
93 struct X11Handle* hdl;
94
95 TSMFGstreamerDecoder* decoder = user_data;
96
97 if (GST_MESSAGE_TYPE(message) != GST_MESSAGE_ELEMENT)
98 return GST_BUS_PASS;
99
100#if GST_VERSION_MAJOR > 0
101 if (!gst_is_video_overlay_prepare_window_handle_message(message))
102 return GST_BUS_PASS;
103#else
104 if (!gst_structure_has_name(message->structure, "prepare-xwindow-id"))
105 return GST_BUS_PASS;
106#endif
107
108 hdl = (struct X11Handle*)decoder->platform;
109
110 if (hdl->subwin)
111 {
112#if GST_VERSION_MAJOR > 0
113 hdl->overlay = GST_VIDEO_OVERLAY(GST_MESSAGE_SRC(message));
114 gst_video_overlay_set_window_handle(hdl->overlay, hdl->subwin);
115 gst_video_overlay_handle_events(hdl->overlay, FALSE);
116#else
117 hdl->overlay = GST_X_OVERLAY(GST_MESSAGE_SRC(message));
118#if GST_CHECK_VERSION(0, 10, 31)
119 gst_x_overlay_set_window_handle(hdl->overlay, hdl->subwin);
120#else
121 gst_x_overlay_set_xwindow_id(hdl->overlay, hdl->subwin);
122#endif
123 gst_x_overlay_handle_events(hdl->overlay, TRUE);
124#endif
125
126 if (hdl->subwinWidth != -1 && hdl->subwinHeight != -1 && hdl->subwinX != -1 &&
127 hdl->subwinY != -1)
128 {
129#if GST_VERSION_MAJOR > 0
130 if (!gst_video_overlay_set_render_rectangle(hdl->overlay, 0, 0, hdl->subwinWidth,
131 hdl->subwinHeight))
132 {
133 WLog_ERR(TAG, "Could not resize overlay!");
134 }
135
136 gst_video_overlay_expose(hdl->overlay);
137#else
138 if (!gst_x_overlay_set_render_rectangle(hdl->overlay, 0, 0, hdl->subwinWidth,
139 hdl->subwinHeight))
140 {
141 WLog_ERR(TAG, "Could not resize overlay!");
142 }
143
144 gst_x_overlay_expose(hdl->overlay);
145#endif
146 XLockDisplay(hdl->disp);
147 XMoveResizeWindow(hdl->disp, hdl->subwin, hdl->subwinX, hdl->subwinY, hdl->subwinWidth,
148 hdl->subwinHeight);
149 XSync(hdl->disp, FALSE);
150 XUnlockDisplay(hdl->disp);
151 }
152 }
153 else
154 {
155 g_warning("Window was not available before retrieving the overlay!");
156 }
157
158 gst_message_unref(message);
159
160 return GST_BUS_DROP;
161}
162
163const char* tsmf_platform_get_video_sink(void)
164{
165 return "autovideosink";
166}
167
168const char* tsmf_platform_get_audio_sink(void)
169{
170 return "autoaudiosink";
171}
172
173int tsmf_platform_create(TSMFGstreamerDecoder* decoder)
174{
175 struct X11Handle* hdl;
176
177 if (!decoder)
178 return -1;
179
180 if (decoder->platform)
181 return -1;
182
183 hdl = calloc(1, sizeof(struct X11Handle));
184 if (!hdl)
185 {
186 WLog_ERR(TAG, "Could not allocate handle.");
187 return -1;
188 }
189
190 decoder->platform = hdl;
191 hdl->shmid = shm_open(get_shm_id(), (O_RDWR | O_CREAT), (PROT_READ | PROT_WRITE));
192 if (hdl->shmid == -1)
193 {
194 char ebuffer[256] = WINPR_C_ARRAY_INIT;
195 WLog_ERR(TAG, "failed to get access to shared memory - shmget(%s): %i - %s", get_shm_id(),
196 errno, winpr_strerror(errno, ebuffer, sizeof(ebuffer)));
197 return -2;
198 }
199
200 hdl->xfwin = mmap(0, sizeof(void*), PROT_READ | PROT_WRITE, MAP_SHARED, hdl->shmid, 0);
201 if (hdl->xfwin == MAP_FAILED)
202 {
203 WLog_ERR(TAG, "shmat failed!");
204 return -3;
205 }
206
207 hdl->disp = XOpenDisplay(nullptr);
208 if (!hdl->disp)
209 {
210 WLog_ERR(TAG, "Failed to open display");
211 return -4;
212 }
213
214 hdl->subwinMapped = FALSE;
215 hdl->subwinX = -1;
216 hdl->subwinY = -1;
217 hdl->subwinWidth = -1;
218 hdl->subwinHeight = -1;
219
220 return 0;
221}
222
223int tsmf_platform_set_format(TSMFGstreamerDecoder* decoder)
224{
225 if (!decoder)
226 return -1;
227
228 if (decoder->media_type == TSMF_MAJOR_TYPE_VIDEO)
229 {
230 }
231
232 return 0;
233}
234
235int tsmf_platform_register_handler(TSMFGstreamerDecoder* decoder)
236{
237 GstBus* bus;
238
239 if (!decoder)
240 return -1;
241
242 if (!decoder->pipe)
243 return -1;
244
245 bus = gst_pipeline_get_bus(GST_PIPELINE(decoder->pipe));
246
247#if GST_VERSION_MAJOR > 0
248 gst_bus_set_sync_handler(bus, (GstBusSyncHandler)tsmf_platform_bus_sync_handler, decoder,
249 nullptr);
250#else
251 gst_bus_set_sync_handler(bus, (GstBusSyncHandler)tsmf_platform_bus_sync_handler, decoder);
252#endif
253
254 if (!bus)
255 {
256 WLog_ERR(TAG, "gst_pipeline_get_bus failed!");
257 return 1;
258 }
259
260 gst_object_unref(bus);
261
262 return 0;
263}
264
265int tsmf_platform_free(TSMFGstreamerDecoder* decoder)
266{
267 struct X11Handle* hdl = decoder->platform;
268
269 if (!hdl)
270 return -1;
271
272 if (hdl->disp)
273 XCloseDisplay(hdl->disp);
274
275 if (hdl->xfwin)
276 munmap(0, sizeof(void*));
277
278 if (hdl->shmid >= 0)
279 close(hdl->shmid);
280
281 free(hdl);
282 decoder->platform = nullptr;
283
284 return 0;
285}
286
287int tsmf_window_create(TSMFGstreamerDecoder* decoder)
288{
289 struct X11Handle* hdl;
290
291 if (decoder->media_type != TSMF_MAJOR_TYPE_VIDEO)
292 {
293 decoder->ready = TRUE;
294 return -3;
295 }
296 else
297 {
298 if (!decoder)
299 return -1;
300
301 if (!decoder->platform)
302 return -1;
303
304 hdl = (struct X11Handle*)decoder->platform;
305
306 if (!hdl->subwin)
307 {
308 XLockDisplay(hdl->disp);
309 hdl->subwin = XCreateSimpleWindow(hdl->disp, *(int*)hdl->xfwin, 0, 0, 1, 1, 0, 0, 0);
310 XUnlockDisplay(hdl->disp);
311
312 if (!hdl->subwin)
313 {
314 WLog_ERR(TAG, "Could not create subwindow!");
315 }
316 }
317
318 tsmf_window_map(decoder);
319
320 decoder->ready = TRUE;
321#if defined(WITH_XEXT)
322 int event, error;
323 XLockDisplay(hdl->disp);
324 hdl->has_shape = XShapeQueryExtension(hdl->disp, &event, &error);
325 XUnlockDisplay(hdl->disp);
326#endif
327 }
328
329 return 0;
330}
331
332int tsmf_window_resize(TSMFGstreamerDecoder* decoder, int x, int y, int width, int height,
333 int nr_rects, RDP_RECT* rects)
334{
335 struct X11Handle* hdl;
336
337 if (!decoder)
338 return -1;
339
340 if (decoder->media_type != TSMF_MAJOR_TYPE_VIDEO)
341 {
342 return -3;
343 }
344
345 if (!decoder->platform)
346 return -1;
347
348 hdl = (struct X11Handle*)decoder->platform;
349 DEBUG_TSMF("resize: x=%d, y=%d, w=%d, h=%d", x, y, width, height);
350
351 if (hdl->overlay)
352 {
353#if GST_VERSION_MAJOR > 0
354
355 if (!gst_video_overlay_set_render_rectangle(hdl->overlay, 0, 0, width, height))
356 {
357 WLog_ERR(TAG, "Could not resize overlay!");
358 }
359
360 gst_video_overlay_expose(hdl->overlay);
361#else
362 if (!gst_x_overlay_set_render_rectangle(hdl->overlay, 0, 0, width, height))
363 {
364 WLog_ERR(TAG, "Could not resize overlay!");
365 }
366
367 gst_x_overlay_expose(hdl->overlay);
368#endif
369 }
370
371 if (hdl->subwin)
372 {
373 hdl->subwinX = x;
374 hdl->subwinY = y;
375 hdl->subwinWidth = width;
376 hdl->subwinHeight = height;
377
378 XLockDisplay(hdl->disp);
379 XMoveResizeWindow(hdl->disp, hdl->subwin, hdl->subwinX, hdl->subwinY, hdl->subwinWidth,
380 hdl->subwinHeight);
381
382 /* Unmap the window if there are no visibility rects */
383 if (nr_rects == 0)
384 tsmf_window_unmap(decoder);
385 else
386 tsmf_window_map(decoder);
387
388#if defined(WITH_XEXT)
389 if (hdl->has_shape)
390 {
391 XRectangle* xrects = nullptr;
392
393 if (nr_rects == 0)
394 {
395 xrects = calloc(1, sizeof(XRectangle));
396 xrects->x = x;
397 xrects->y = y;
398 xrects->width = width;
399 xrects->height = height;
400 }
401 else
402 {
403 xrects = calloc(nr_rects, sizeof(XRectangle));
404 }
405
406 if (xrects)
407 {
408 for (int i = 0; i < nr_rects; i++)
409 {
410 xrects[i].x = rects[i].x - x;
411 xrects[i].y = rects[i].y - y;
412 xrects[i].width = rects[i].width;
413 xrects[i].height = rects[i].height;
414 }
415
416 XShapeCombineRectangles(hdl->disp, hdl->subwin, ShapeBounding, x, y, xrects,
417 nr_rects, ShapeSet, 0);
418 free(xrects);
419 }
420 }
421#endif
422 XSync(hdl->disp, FALSE);
423 XUnlockDisplay(hdl->disp);
424 }
425
426 return 0;
427}
428
429int tsmf_window_map(TSMFGstreamerDecoder* decoder)
430{
431 struct X11Handle* hdl;
432 if (!decoder)
433 return -1;
434
435 hdl = (struct X11Handle*)decoder->platform;
436
437 /* Only need to map the window if it is not currently mapped */
438 if ((hdl->subwin) && (!hdl->subwinMapped))
439 {
440 XLockDisplay(hdl->disp);
441 XMapWindow(hdl->disp, hdl->subwin);
442 hdl->subwinMapped = TRUE;
443 XSync(hdl->disp, FALSE);
444 XUnlockDisplay(hdl->disp);
445 }
446
447 return 0;
448}
449
450int tsmf_window_unmap(TSMFGstreamerDecoder* decoder)
451{
452 struct X11Handle* hdl;
453 if (!decoder)
454 return -1;
455
456 hdl = (struct X11Handle*)decoder->platform;
457
458 /* only need to unmap window if it is currently mapped */
459 if ((hdl->subwin) && (hdl->subwinMapped))
460 {
461 XLockDisplay(hdl->disp);
462 XUnmapWindow(hdl->disp, hdl->subwin);
463 hdl->subwinMapped = FALSE;
464 XSync(hdl->disp, FALSE);
465 XUnlockDisplay(hdl->disp);
466 }
467
468 return 0;
469}
470
471int tsmf_window_destroy(TSMFGstreamerDecoder* decoder)
472{
473 struct X11Handle* hdl;
474
475 if (!decoder)
476 return -1;
477
478 decoder->ready = FALSE;
479
480 if (decoder->media_type != TSMF_MAJOR_TYPE_VIDEO)
481 return -3;
482
483 if (!decoder->platform)
484 return -1;
485
486 hdl = (struct X11Handle*)decoder->platform;
487
488 if (hdl->subwin)
489 {
490 XLockDisplay(hdl->disp);
491 XDestroyWindow(hdl->disp, hdl->subwin);
492 XSync(hdl->disp, FALSE);
493 XUnlockDisplay(hdl->disp);
494 }
495
496 hdl->overlay = nullptr;
497 hdl->subwin = 0;
498 hdl->subwinMapped = FALSE;
499 hdl->subwinX = -1;
500 hdl->subwinY = -1;
501 hdl->subwinWidth = -1;
502 hdl->subwinHeight = -1;
503 return 0;
504}