ximagesink.c 79.2 KB
Newer Older
Julien Moutte's avatar
Julien Moutte committed
1
/* GStreamer
2
 * Copyright (C) <2005> Julien Moutte <julien@moutte.net>
Julien Moutte's avatar
Julien Moutte committed
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
19

20
21
22
23
24
25
26
27
/**
 * SECTION:element-ximagesink
 *
 * XImageSink renders video frames to a drawable (XWindow) on a local or remote
 * display. This element can receive a Window ID from the application through
 * the XOverlay interface and will then render video frames in this drawable.
 * If no Window ID was provided by the application, the element will create its
 * own internal window and render into it.
28
29
 *
 * <refsect2>
30
31
32
33
34
35
36
37
38
 * <title>Scaling</title>
 * <para>
 * As standard XImage rendering to a drawable is not scaled, XImageSink will use
 * reverse caps negotiation to try to get scaled video frames for the drawable.
 * This is accomplished by asking the peer pad if it accepts some different caps
 * which in most cases implies that there is a scaling element in the pipeline,
 * or that an element generating the video frames can generate them with a 
 * different geometry. This mechanism is handled during buffer allocations, for
 * each allocation request the video sink will check the drawable geometry, look
39
40
41
42
 * at the #GstXImageSink:force-aspect-ratio property, calculate the geometry of
 * desired video frames and then check that the peer pad accept those new caps.
 * If it does it will then allocate a buffer in video memory with this new
 * geometry and return it with the new caps.
43
 * </para>
44
45
 * </refsect2>
 * <refsect2>
46
47
48
49
50
51
52
53
54
55
56
57
 * <title>Events</title>
 * <para>
 * XImageSink creates a thread to handle events coming from the drawable. There
 * are several kind of events that can be grouped in 2 big categories: input 
 * events and window state related events. Input events will be translated to
 * navigation events and pushed upstream for other elements to react on them.
 * This includes events such as pointer moves, key press/release, clicks etc...
 * Other events are used to handle the drawable appearance even when the data
 * is not flowing (GST_STATE_PAUSED). That means that even when the element is
 * paused, it will receive expose events from the drawable and draw the latest
 * frame with correct borders/aspect-ratio.
 * </para>
58
59
 * </refsect2>
 * <refsect2>
60
61
62
 * <title>Pixel aspect ratio</title>
 * <para>
 * When changing state to GST_STATE_READY, XImageSink will open a connection to
63
 * the display specified in the #GstXImageSink:display property or the default
64
65
66
67
68
69
70
 * display if nothing specified. Once this connection is open it will inspect 
 * the display configuration including the physical display geometry and 
 * then calculate the pixel aspect ratio. When caps negotiation will occur, the
 * video sink will set the calculated pixel aspect ratio on the caps to make 
 * sure that incoming video frames will have the correct pixel aspect ratio for
 * this display. Sometimes the calculated pixel aspect ratio can be wrong, it is
 * then possible to enforce a specific pixel aspect ratio using the
71
 * #GstXImageSink:pixel-aspect-ratio property.
72
 * </para>
73
74
 * </refsect2>
 * <refsect2>
75
 * <title>Examples</title>
76
 * |[
77
 * gst-launch -v videotestsrc ! queue ! ximagesink
78
79
80
81
82
83
 * ]| A pipeline to test reverse negotiation. When the test video signal appears
 * you can resize the window and see that scaled buffers of the desired size are
 * going to arrive with a short delay. This illustrates how buffers of desired
 * size are allocated along the way. If you take away the queue, scaling will
 * happen almost immediately.
 * |[
84
 * gst-launch -v videotestsrc ! navigationtest ! ffmpegcolorspace ! ximagesink
85
 * ]| A pipeline to test navigation events.
86
87
88
89
90
 * While moving the mouse pointer over the test signal you will see a black box
 * following the mouse pointer. If you press the mouse button somewhere on the 
 * video and release it somewhere else a green box will appear where you pressed
 * the button and a red one where you released it. (The navigationtest element
 * is part of gst-plugins-good.)
91
 * |[
92
 * gst-launch -v videotestsrc ! video/x-raw-rgb, pixel-aspect-ratio=(fraction)4/3 ! videoscale ! ximagesink
93
 * ]| This is faking a 4/3 pixel aspect ratio caps on video frames produced by
94
95
96
97
98
99
100
101
 * videotestsrc, in most cases the pixel aspect ratio of the display will be
 * 1/1. This means that videoscale will have to do the scaling to convert 
 * incoming frames to a size that will match the display pixel aspect ratio
 * (from 320x240 to 320x180 in this case). Note that you might have to escape 
 * some characters for your shell like '\(fraction\)'.
 * </refsect2>
 */

Julien Moutte's avatar
Julien Moutte committed
102
103
104
105
106
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

/* Our interfaces */
107
108
#include <gst/interfaces/navigation.h>
#include <gst/interfaces/xoverlay.h>
Julien Moutte's avatar
Julien Moutte committed
109
110
111
112

/* Object header */
#include "ximagesink.h"

113
114
/* Debugging category */
#include <gst/gstinfo.h>
115
116

GST_DEBUG_CATEGORY_EXTERN (gst_debug_ximagesink);
117
118
#define GST_CAT_DEFAULT gst_debug_ximagesink

119
120
121
122
123
124
125
126
127
128
129
130
typedef struct
{
  unsigned long flags;
  unsigned long functions;
  unsigned long decorations;
  long input_mode;
  unsigned long status;
}
MotifWmHints, MwmHints;

#define MWM_HINTS_DECORATIONS   (1L << 1)

131
static void gst_ximagesink_reset (GstXImageSink * ximagesink);
132
static void gst_ximagesink_ximage_destroy (GstXImageSink * ximagesink,
133
    GstXImageBuffer * ximage);
134
static void gst_ximagesink_xwindow_update_geometry (GstXImageSink * ximagesink);
135
static void gst_ximagesink_expose (GstXOverlay * overlay);
136

David Schleef's avatar
David Schleef committed
137
static GstStaticPadTemplate gst_ximagesink_sink_template_factory =
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
138
139
140
141
GST_STATIC_PAD_TEMPLATE ("sink",
    GST_PAD_SINK,
    GST_PAD_ALWAYS,
    GST_STATIC_CAPS ("video/x-raw-rgb, "
142
        "framerate = (fraction) [ 0, MAX ], "
143
        "width = (int) [ 1, MAX ], " "height = (int) [ 1, MAX ]")
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
144
145
146
147
    );

enum
{
148
149
150
  PROP_0,
  PROP_DISPLAY,
  PROP_SYNCHRONOUS,
151
  PROP_PIXEL_ASPECT_RATIO,
152
  PROP_FORCE_ASPECT_RATIO,
153
  PROP_HANDLE_EVENTS,
154
155
156
  PROP_HANDLE_EXPOSE,
  PROP_WINDOW_WIDTH,
  PROP_WINDOW_HEIGHT
157
158
};

159
static GstVideoSinkClass *parent_class = NULL;
Julien Moutte's avatar
Julien Moutte committed
160
161
162
163
164
165
166

/* ============================================================= */
/*                                                               */
/*                       Private Methods                         */
/*                                                               */
/* ============================================================= */

167
168
/* ximage buffers */

169
170
static GstBufferClass *ximage_buffer_parent_class = NULL;

171
172
173
174
#define GST_TYPE_XIMAGE_BUFFER (gst_ximage_buffer_get_type())

#define GST_IS_XIMAGE_BUFFER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_XIMAGE_BUFFER))
#define GST_XIMAGE_BUFFER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_XIMAGE_BUFFER, GstXImageBuffer))
175
176
177
178
179
180
181
182
183
#define GST_XIMAGE_BUFFER_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_XIMAGE_BUFFER, GstXImageBufferClass))

/* So some words about GstMiniObject, this is pretty messy...
   GstMiniObject does not use the standard finalizing of GObjects, you are 
   supposed to call gst_buffer_unref that's going to call gst_mini_objec_unref
   which will handle its own refcount system and call gst_mini_object_free.
   gst_mini_object_free will call the class finalize method which is not the 
   one from GObject, after calling this finalize method it will free the object
   instance for you if the refcount is still 0 so you should not chain up */
184
static void
185
gst_ximage_buffer_finalize (GstXImageBuffer * ximage)
186
{
187
188
  GstXImageSink *ximagesink = NULL;
  gboolean recycled = FALSE;
189
  gboolean running;
190

191
  g_return_if_fail (ximage != NULL);
192

193
  ximagesink = ximage->ximagesink;
194
  if (G_UNLIKELY (ximagesink == NULL)) {
195
196
    GST_WARNING_OBJECT (ximagesink, "no sink found");
    goto beach;
197
198
  }

199
200
201
202
203
204
205
206
207
208
  GST_OBJECT_LOCK (ximagesink);
  running = ximagesink->running;
  GST_OBJECT_UNLOCK (ximagesink);

  if (running == FALSE) {
    /* If the sink is shutting down, need to clear the image */
    GST_DEBUG_OBJECT (ximagesink,
        "destroy image %p because the sink is shutting down", ximage);
    gst_ximagesink_ximage_destroy (ximagesink, ximage);
  } else if ((ximage->width != GST_VIDEO_SINK_WIDTH (ximagesink)) ||
209
      (ximage->height != GST_VIDEO_SINK_HEIGHT (ximagesink))) {
210
    /* If our geometry changed we can't reuse that image. */
211
212
213
214
215
216
217
    GST_DEBUG_OBJECT (ximagesink,
        "destroy image %p as its size changed %dx%d vs current %dx%d",
        ximage, ximage->width, ximage->height,
        GST_VIDEO_SINK_WIDTH (ximagesink), GST_VIDEO_SINK_HEIGHT (ximagesink));
    gst_ximagesink_ximage_destroy (ximagesink, ximage);
  } else {
    /* In that case we can reuse the image and add it to our image pool. */
218
    GST_LOG_OBJECT (ximagesink, "recycling image %p in pool", ximage);
219
    /* need to increment the refcount again to recycle */
220
    gst_buffer_ref (GST_BUFFER_CAST (ximage));
221
222
223
224
    g_mutex_lock (ximagesink->pool_lock);
    ximagesink->buffer_pool = g_slist_prepend (ximagesink->buffer_pool, ximage);
    g_mutex_unlock (ximagesink->pool_lock);
    recycled = TRUE;
225
226
  }

227
  if (!recycled)
Wim Taymans's avatar
Wim Taymans committed
228
229
    GST_MINI_OBJECT_CLASS (ximage_buffer_parent_class)->finalize
        (GST_MINI_OBJECT (ximage));
230

231
232
beach:
  return;
233
234
}

235
236
237
238
239
240
static void
gst_ximage_buffer_free (GstXImageBuffer * ximage)
{
  /* make sure it is not recycled */
  ximage->width = -1;
  ximage->height = -1;
241
  gst_buffer_unref (GST_BUFFER_CAST (ximage));
242
243
}

244
static void
245
gst_ximage_buffer_init (GstXImageBuffer * ximage_buffer, gpointer g_class)
246
{
247
#ifdef HAVE_XSHM
248
249
  ximage_buffer->SHMInfo.shmaddr = ((void *) -1);
  ximage_buffer->SHMInfo.shmid = -1;
250
#endif
251
252
253
254
255
256
257
}

static void
gst_ximage_buffer_class_init (gpointer g_class, gpointer class_data)
{
  GstMiniObjectClass *mini_object_class = GST_MINI_OBJECT_CLASS (g_class);

258
259
  ximage_buffer_parent_class = g_type_class_peek_parent (g_class);

260
261
262
263
  mini_object_class->finalize = (GstMiniObjectFinalizeFunction)
      gst_ximage_buffer_finalize;
}

264
static GType
265
266
267
268
269
270
271
272
273
274
275
276
277
278
gst_ximage_buffer_get_type (void)
{
  static GType _gst_ximage_buffer_type;

  if (G_UNLIKELY (_gst_ximage_buffer_type == 0)) {
    static const GTypeInfo ximage_buffer_info = {
      sizeof (GstBufferClass),
      NULL,
      NULL,
      gst_ximage_buffer_class_init,
      NULL,
      NULL,
      sizeof (GstXImageBuffer),
      0,
279
      (GInstanceInitFunc) gst_ximage_buffer_init,
280
281
282
283
284
285
286
287
      NULL
    };
    _gst_ximage_buffer_type = g_type_register_static (GST_TYPE_BUFFER,
        "GstXImageBuffer", &ximage_buffer_info, 0);
  }
  return _gst_ximage_buffer_type;
}

Julien Moutte's avatar
Julien Moutte committed
288
289
/* X11 stuff */

290
291
static gboolean error_caught = FALSE;

Julien Moutte's avatar
Julien Moutte committed
292
static int
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
293
gst_ximagesink_handle_xerror (Display * display, XErrorEvent * xevent)
Julien Moutte's avatar
Julien Moutte committed
294
{
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
295
296
  char error_msg[1024];

Julien Moutte's avatar
Julien Moutte committed
297
  XGetErrorText (display, xevent->error_code, error_msg, 1024);
298
  GST_DEBUG ("ximagesink triggered an XError. error: %s", error_msg);
299
  error_caught = TRUE;
Julien Moutte's avatar
Julien Moutte committed
300
301
302
  return 0;
}

303
304
#ifdef HAVE_XSHM                /* Check that XShm calls actually work */

Julien Moutte's avatar
Julien Moutte committed
305
static gboolean
306
307
gst_ximagesink_check_xshm_calls (GstXImageSink * ximagesink,
    GstXContext * xcontext)
Julien Moutte's avatar
Julien Moutte committed
308
{
309
310
311
  XImage *ximage;
  XShmSegmentInfo SHMInfo;
  size_t size;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
312
  int (*handler) (Display *, XErrorEvent *);
313
  gboolean result = FALSE;
314
  gboolean did_attach = FALSE;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
315

Julien Moutte's avatar
Julien Moutte committed
316
  g_return_val_if_fail (xcontext != NULL, FALSE);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
317

318
319
320
321
322
323
324
  /* Sync to ensure any older errors are already processed */
  XSync (xcontext->disp, FALSE);

  /* Set defaults so we don't free these later unnecessarily */
  SHMInfo.shmaddr = ((void *) -1);
  SHMInfo.shmid = -1;

Julien Moutte's avatar
Julien Moutte committed
325
  /* Setting an error handler to catch failure */
326
  error_caught = FALSE;
Julien Moutte's avatar
Julien Moutte committed
327
  handler = XSetErrorHandler (gst_ximagesink_handle_xerror);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
328

329
  /* Trying to create a 1x1 ximage */
330
331
  GST_DEBUG ("XShmCreateImage of 1x1");

332
333
  ximage = XShmCreateImage (xcontext->disp, xcontext->visual,
      xcontext->depth, ZPixmap, NULL, &SHMInfo, 1, 1);
334
335
336
337

  /* Might cause an error, sync to ensure it is noticed */
  XSync (xcontext->disp, FALSE);
  if (!ximage || error_caught) {
338
339
340
    GST_WARNING ("could not XShmCreateImage a 1x1 image");
    goto beach;
  }
341
  size = ximage->height * ximage->bytes_per_line;
Julien Moutte's avatar
Julien Moutte committed
342

343
344
  SHMInfo.shmid = shmget (IPC_PRIVATE, size, IPC_CREAT | 0777);
  if (SHMInfo.shmid == -1) {
345
346
    GST_WARNING ("could not get shared memory of %" G_GSIZE_FORMAT " bytes",
        size);
347
348
349
    goto beach;
  }

350
  SHMInfo.shmaddr = shmat (SHMInfo.shmid, NULL, 0);
351
  if (SHMInfo.shmaddr == ((void *) -1)) {
352
    GST_WARNING ("Failed to shmat: %s", g_strerror (errno));
353
    /* Clean up shm seg */
354
    shmctl (SHMInfo.shmid, IPC_RMID, NULL);
355
356
357
    goto beach;
  }

358
359
  ximage->data = SHMInfo.shmaddr;
  SHMInfo.readOnly = FALSE;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
360

361
  if (XShmAttach (xcontext->disp, &SHMInfo) == 0) {
362
    GST_WARNING ("Failed to XShmAttach");
Jan Schmidt's avatar
Jan Schmidt committed
363
364
    /* Clean up shm seg */
    shmctl (SHMInfo.shmid, IPC_RMID, NULL);
365
366
    goto beach;
  }
Julien Moutte's avatar
Julien Moutte committed
367

368
369
370
  /* Sync to ensure we see any errors we caused */
  XSync (xcontext->disp, FALSE);

Jan Schmidt's avatar
Jan Schmidt committed
371
372
373
374
375
  /* Delete the shared memory segment as soon as everyone is attached. 
   * This way, it will be deleted as soon as we detach later, and not
   * leaked if we crash. */
  shmctl (SHMInfo.shmid, IPC_RMID, NULL);

376
377
378
379
380
381
382
383
384
385
386
  if (!error_caught) {
    did_attach = TRUE;
    /* store whether we succeeded in result */
    result = TRUE;
  }

beach:
  /* Sync to ensure we swallow any errors we caused and reset error_caught */
  XSync (xcontext->disp, FALSE);
  error_caught = FALSE;
  XSetErrorHandler (handler);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
387

388
  if (did_attach) {
389
    XShmDetach (xcontext->disp, &SHMInfo);
390
    XSync (xcontext->disp, FALSE);
391
  }
392
393
  if (SHMInfo.shmaddr != ((void *) -1))
    shmdt (SHMInfo.shmaddr);
394
395
  if (ximage)
    XDestroyImage (ximage);
396
  return result;
Julien Moutte's avatar
Julien Moutte committed
397
}
398
#endif /* HAVE_XSHM */
Julien Moutte's avatar
Julien Moutte committed
399

400
401
/* This function handles GstXImageBuffer creation depending on XShm availability */
static GstXImageBuffer *
402
gst_ximagesink_ximage_new (GstXImageSink * ximagesink, GstCaps * caps)
Julien Moutte's avatar
Julien Moutte committed
403
{
404
  GstXImageBuffer *ximage = NULL;
405
  GstStructure *structure = NULL;
406
  gboolean succeeded = FALSE;
407
  int (*handler) (Display *, XErrorEvent *);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
408

Julien Moutte's avatar
Julien Moutte committed
409
  g_return_val_if_fail (GST_IS_XIMAGESINK (ximagesink), NULL);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
410

411
  ximage = (GstXImageBuffer *) gst_mini_object_new (GST_TYPE_XIMAGE_BUFFER);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
412

413
414
415
416
417
418
419
420
421
  structure = gst_caps_get_structure (caps, 0);

  if (!gst_structure_get_int (structure, "width", &ximage->width) ||
      !gst_structure_get_int (structure, "height", &ximage->height)) {
    GST_WARNING ("failed getting geometry from caps %" GST_PTR_FORMAT, caps);
  }

  GST_DEBUG_OBJECT (ximagesink, "creating image %p (%dx%d)", ximage,
      ximage->width, ximage->height);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
422

Julien Moutte's avatar
Julien Moutte committed
423
424
  g_mutex_lock (ximagesink->x_lock);

425
426
427
428
  /* Setting an error handler to catch failure */
  error_caught = FALSE;
  handler = XSetErrorHandler (gst_ximagesink_handle_xerror);

429
#ifdef HAVE_XSHM
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
430
431
  if (ximagesink->xcontext->use_xshm) {
    ximage->ximage = XShmCreateImage (ximagesink->xcontext->disp,
432
433
434
        ximagesink->xcontext->visual,
        ximagesink->xcontext->depth,
        ZPixmap, NULL, &ximage->SHMInfo, ximage->width, ximage->height);
435
    if (!ximage->ximage || error_caught) {
436
      g_mutex_unlock (ximagesink->x_lock);
437
438
439
440
      /* Reset error handler */
      error_caught = FALSE;
      XSetErrorHandler (handler);
      /* Push an error */
441
442
443
444
445
      GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
          ("Failed to create output image buffer of %dx%d pixels",
              ximage->width, ximage->height),
          ("could not XShmCreateImage a %dx%d image",
              ximage->width, ximage->height));
446
447
448
449
      goto beach;
    }

    /* we have to use the returned bytes_per_line for our shm size */
450
    ximage->size = ximage->ximage->bytes_per_line * ximage->ximage->height;
451
452
    GST_LOG_OBJECT (ximagesink,
        "XShm image size is %" G_GSIZE_FORMAT ", width %d, stride %d",
453
        ximage->size, ximage->width, ximage->ximage->bytes_per_line);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
454
455

    ximage->SHMInfo.shmid = shmget (IPC_PRIVATE, ximage->size,
456
        IPC_CREAT | 0777);
457
    if (ximage->SHMInfo.shmid == -1) {
458
      g_mutex_unlock (ximagesink->x_lock);
459
460
461
      GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
          ("Failed to create output image buffer of %dx%d pixels",
              ximage->width, ximage->height),
462
463
          ("could not get shared memory of %" G_GSIZE_FORMAT " bytes",
              ximage->size));
464
465
      goto beach;
    }
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
466

467
    ximage->SHMInfo.shmaddr = shmat (ximage->SHMInfo.shmid, NULL, 0);
468
    if (ximage->SHMInfo.shmaddr == ((void *) -1)) {
469
      g_mutex_unlock (ximagesink->x_lock);
470
471
472
      GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
          ("Failed to create output image buffer of %dx%d pixels",
              ximage->width, ximage->height),
473
          ("Failed to shmat: %s", g_strerror (errno)));
474
      /* Clean up the shared memory segment */
475
      shmctl (ximage->SHMInfo.shmid, IPC_RMID, NULL);
476
477
      goto beach;
    }
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
478

479
    ximage->ximage->data = ximage->SHMInfo.shmaddr;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
480
481
    ximage->SHMInfo.readOnly = FALSE;

482
    if (XShmAttach (ximagesink->xcontext->disp, &ximage->SHMInfo) == 0) {
Jan Schmidt's avatar
Jan Schmidt committed
483
484
485
      /* Clean up shm seg */
      shmctl (ximage->SHMInfo.shmid, IPC_RMID, NULL);

486
      g_mutex_unlock (ximagesink->x_lock);
487
488
489
      GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
          ("Failed to create output image buffer of %dx%d pixels",
              ximage->width, ximage->height), ("Failed to XShmAttach"));
490
491
      goto beach;
    }
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
492
493

    XSync (ximagesink->xcontext->disp, FALSE);
Jan Schmidt's avatar
Jan Schmidt committed
494
495
496
497
498
499

    /* Now that everyone has attached, we can delete the shared memory segment.
     * This way, it will be deleted as soon as we detach later, and not
     * leaked if we crash. */
    shmctl (ximage->SHMInfo.shmid, IPC_RMID, NULL);

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
500
  } else
501
#endif /* HAVE_XSHM */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
502
  {
503
504
    guint allocsize;

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
505
    ximage->ximage = XCreateImage (ximagesink->xcontext->disp,
506
507
        ximagesink->xcontext->visual,
        ximagesink->xcontext->depth,
508
        ZPixmap, 0, NULL,
509
        ximage->width, ximage->height, ximagesink->xcontext->bpp, 0);
510
    if (!ximage->ximage || error_caught) {
511
      g_mutex_unlock (ximagesink->x_lock);
512
513
514
515
      /* Reset error handler */
      error_caught = FALSE;
      XSetErrorHandler (handler);
      /* Push an error */
516
517
518
519
520
      GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
          ("Failed to create output image buffer of %dx%d pixels",
              ximage->width, ximage->height),
          ("could not XCreateImage a %dx%d image",
              ximage->width, ximage->height));
521
522
      goto beach;
    }
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
523

524
525
526
527
528
529
530
    /* upstream will assume that rowstrides are multiples of 4, but this
     * doesn't always seem to be the case with XCreateImage() */
    if ((ximage->ximage->bytes_per_line % 4) != 0) {
      GST_WARNING_OBJECT (ximagesink, "returned stride not a multiple of 4 as "
          "usually assumed");
    }

531
    /* we have to use the returned bytes_per_line for our image size */
532
    ximage->size = ximage->ximage->bytes_per_line * ximage->ximage->height;
533
534
535
536
537
538
539
540
541
542
543
544

    /* alloc a bit more for unexpected strides to avoid crashes upstream.
     * FIXME: if we get an unrounded stride, the image will be displayed
     * distorted, since all upstream elements assume a rounded stride */
    allocsize =
        GST_ROUND_UP_4 (ximage->ximage->bytes_per_line) *
        ximage->ximage->height;
    ximage->ximage->data = g_malloc (allocsize);
    GST_LOG_OBJECT (ximagesink,
        "non-XShm image size is %" G_GSIZE_FORMAT " (alloced: %u), width %d, "
        "stride %d", ximage->size, allocsize, ximage->width,
        ximage->ximage->bytes_per_line);
545

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
546
547
    XSync (ximagesink->xcontext->disp, FALSE);
  }
548
549
550
551
552

  /* Reset error handler */
  error_caught = FALSE;
  XSetErrorHandler (handler);

553
  succeeded = TRUE;
554
555
556
557

  GST_BUFFER_DATA (ximage) = (guchar *) ximage->ximage->data;
  GST_BUFFER_SIZE (ximage) = ximage->size;

558
559
560
  /* Keep a ref to our sink */
  ximage->ximagesink = gst_object_ref (ximagesink);

561
  g_mutex_unlock (ximagesink->x_lock);
562
beach:
563
  if (!succeeded) {
564
    gst_ximage_buffer_free (ximage);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
565
566
567
    ximage = NULL;
  }

Julien Moutte's avatar
Julien Moutte committed
568
569
570
  return ximage;
}

571
/* This function destroys a GstXImageBuffer handling XShm availability */
Julien Moutte's avatar
Julien Moutte committed
572
static void
573
574
gst_ximagesink_ximage_destroy (GstXImageSink * ximagesink,
    GstXImageBuffer * ximage)
Julien Moutte's avatar
Julien Moutte committed
575
576
577
{
  g_return_if_fail (ximage != NULL);
  g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
578

579
580
581
  /* Hold the object lock to ensure the XContext doesn't disappear */
  GST_OBJECT_LOCK (ximagesink);

582
  /* If the destroyed image is the current one we destroy our reference too */
583
  if (ximagesink->cur_image == ximage) {
584
    ximagesink->cur_image = NULL;
585
  }
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
586

587
588
  /* We might have some buffers destroyed after changing state to NULL */
  if (!ximagesink->xcontext) {
589
590
591
592
593
594
    GST_DEBUG_OBJECT (ximagesink, "Destroying XImage after XContext");
#ifdef HAVE_XSHM
    if (ximage->SHMInfo.shmaddr != ((void *) -1)) {
      shmdt (ximage->SHMInfo.shmaddr);
    }
#endif
595
596
597
    goto beach;
  }

Julien Moutte's avatar
Julien Moutte committed
598
  g_mutex_lock (ximagesink->x_lock);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
599

600
#ifdef HAVE_XSHM
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
601
  if (ximagesink->xcontext->use_xshm) {
602
    if (ximage->SHMInfo.shmaddr != ((void *) -1)) {
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
603
      XShmDetach (ximagesink->xcontext->disp, &ximage->SHMInfo);
604
      XSync (ximagesink->xcontext->disp, 0);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
605
      shmdt (ximage->SHMInfo.shmaddr);
606
607
608
609
    }
    if (ximage->ximage)
      XDestroyImage (ximage->ximage);

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
610
611
612
  } else
#endif /* HAVE_XSHM */
  {
613
    if (ximage->ximage) {
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
614
      XDestroyImage (ximage->ximage);
615
    }
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
616
617
  }

618
  XSync (ximagesink->xcontext->disp, FALSE);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
619

Julien Moutte's avatar
Julien Moutte committed
620
  g_mutex_unlock (ximagesink->x_lock);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
621

622
beach:
623
624
  GST_OBJECT_UNLOCK (ximagesink);

625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
  if (ximage->ximagesink) {
    /* Release the ref to our sink */
    ximage->ximagesink = NULL;
    gst_object_unref (ximagesink);
  }

  return;
}

/* We are called with the x_lock taken */
static void
gst_ximagesink_xwindow_draw_borders (GstXImageSink * ximagesink,
    GstXWindow * xwindow, GstVideoRectangle rect)
{
  g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
  g_return_if_fail (xwindow != NULL);

  XSetForeground (ximagesink->xcontext->disp, xwindow->gc,
      ximagesink->xcontext->black);

  /* Left border */
  if (rect.x > 0) {
    XFillRectangle (ximagesink->xcontext->disp, xwindow->win, xwindow->gc,
        0, 0, rect.x, xwindow->height);
  }

  /* Right border */
  if ((rect.x + rect.w) < xwindow->width) {
    XFillRectangle (ximagesink->xcontext->disp, xwindow->win, xwindow->gc,
        rect.x + rect.w, 0, xwindow->width, xwindow->height);
  }

  /* Top border */
  if (rect.y > 0) {
    XFillRectangle (ximagesink->xcontext->disp, xwindow->win, xwindow->gc,
        0, 0, xwindow->width, rect.y);
  }

  /* Bottom border */
  if ((rect.y + rect.h) < xwindow->height) {
    XFillRectangle (ximagesink->xcontext->disp, xwindow->win, xwindow->gc,
        0, rect.y + rect.h, xwindow->width, xwindow->height);
  }
Julien Moutte's avatar
Julien Moutte committed
668
669
}

670
/* This function puts a GstXImageBuffer on a GstXImageSink's window */
671
static gboolean
672
gst_ximagesink_ximage_put (GstXImageSink * ximagesink, GstXImageBuffer * ximage)
Julien Moutte's avatar
Julien Moutte committed
673
{
674
  GstVideoRectangle src, dst, result;
675
  gboolean draw_border = FALSE;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
676

677
  g_return_val_if_fail (GST_IS_XIMAGESINK (ximagesink), FALSE);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
678

679
  /* We take the flow_lock. If expose is in there we don't want to run
680
681
     concurrently from the data flow thread */
  g_mutex_lock (ximagesink->flow_lock);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
682

683
684
  if (G_UNLIKELY (ximagesink->xwindow == NULL)) {
    g_mutex_unlock (ximagesink->flow_lock);
685
    return FALSE;
686
687
  }

688
  /* Draw borders when displaying the first frame. After this
689
690
     draw borders only on expose event or caps change (ximagesink->draw_border = TRUE). */
  if (!ximagesink->cur_image || ximagesink->draw_border) {
691
692
693
    draw_border = TRUE;
  }

694
  /* Store a reference to the last image we put, lose the previous one */
695
  if (ximage && ximagesink->cur_image != ximage) {
696
    if (ximagesink->cur_image) {
697
      GST_LOG_OBJECT (ximagesink, "unreffing %p", ximagesink->cur_image);
698
      gst_buffer_unref (GST_BUFFER_CAST (ximagesink->cur_image));
699
    }
700
    GST_LOG_OBJECT (ximagesink, "reffing %p as our current image", ximage);
701
    ximagesink->cur_image =
702
        GST_XIMAGE_BUFFER (gst_buffer_ref (GST_BUFFER_CAST (ximage)));
703
704
  }

705
706
  /* Expose sends a NULL image, we take the latest frame */
  if (!ximage) {
707
    draw_border = TRUE;
708
709
710
711
    if (ximagesink->cur_image) {
      ximage = ximagesink->cur_image;
    } else {
      g_mutex_unlock (ximagesink->flow_lock);
712
      return TRUE;
713
714
715
    }
  }

716
717
718
719
  src.w = ximage->width;
  src.h = ximage->height;
  dst.w = ximagesink->xwindow->width;
  dst.h = ximagesink->xwindow->height;
Julien Moutte's avatar
Julien Moutte committed
720

721
  gst_video_sink_center_rect (src, dst, &result, FALSE);
722

723
  g_mutex_lock (ximagesink->x_lock);
724

725
726
727
  if (draw_border) {
    gst_ximagesink_xwindow_draw_borders (ximagesink, ximagesink->xwindow,
        result);
728
    ximagesink->draw_border = FALSE;
729
  }
730
#ifdef HAVE_XSHM
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
731
  if (ximagesink->xcontext->use_xshm) {
732
    GST_LOG_OBJECT (ximagesink,
733
734
735
        "XShmPutImage on %p, src: %d, %d - dest: %d, %d, dim: %dx%d, win %dx%d",
        ximage, 0, 0, result.x, result.y, result.w, result.h,
        ximagesink->xwindow->width, ximagesink->xwindow->height);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
736
    XShmPutImage (ximagesink->xcontext->disp, ximagesink->xwindow->win,
737
738
        ximagesink->xwindow->gc, ximage->ximage, 0, 0, result.x, result.y,
        result.w, result.h, FALSE);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
739
  } else
Julien Moutte's avatar
Julien Moutte committed
740
#endif /* HAVE_XSHM */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
741
  {
742
743
744
745
    GST_LOG_OBJECT (ximagesink,
        "XPutImage on %p, src: %d, %d - dest: %d, %d, dim: %dx%d, win %dx%d",
        ximage, 0, 0, result.x, result.y, result.w, result.h,
        ximagesink->xwindow->width, ximagesink->xwindow->height);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
746
    XPutImage (ximagesink->xcontext->disp, ximagesink->xwindow->win,
747
748
        ximagesink->xwindow->gc, ximage->ximage, 0, 0, result.x, result.y,
        result.w, result.h);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
749
750
  }

751
  XSync (ximagesink->xcontext->disp, FALSE);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
752

Julien Moutte's avatar
Julien Moutte committed
753
  g_mutex_unlock (ximagesink->x_lock);
754
755

  g_mutex_unlock (ximagesink->flow_lock);
756
757

  return TRUE;
Julien Moutte's avatar
Julien Moutte committed
758
759
}

760
761
762
763
764
765
766
767
768
769
static gboolean
gst_ximagesink_xwindow_decorate (GstXImageSink * ximagesink,
    GstXWindow * window)
{
  Atom hints_atom = None;
  MotifWmHints *hints;

  g_return_val_if_fail (GST_IS_XIMAGESINK (ximagesink), FALSE);
  g_return_val_if_fail (window != NULL, FALSE);

770
771
  g_mutex_lock (ximagesink->x_lock);

772
  hints_atom = XInternAtom (ximagesink->xcontext->disp, "_MOTIF_WM_HINTS", 1);
773
  if (hints_atom == None) {
774
    g_mutex_unlock (ximagesink->x_lock);
775
776
777
    return FALSE;
  }

778
779
  hints = g_malloc0 (sizeof (MotifWmHints));

780
781
782
783
784
785
786
787
788
  hints->flags |= MWM_HINTS_DECORATIONS;
  hints->decorations = 1 << 0;

  XChangeProperty (ximagesink->xcontext->disp, window->win,
      hints_atom, hints_atom, 32, PropModeReplace,
      (guchar *) hints, sizeof (MotifWmHints) / sizeof (long));

  XSync (ximagesink->xcontext->disp, FALSE);

789
790
  g_mutex_unlock (ximagesink->x_lock);

791
792
793
794
795
  g_free (hints);

  return TRUE;
}

796
797
static void
gst_ximagesink_xwindow_set_title (GstXImageSink * ximagesink,
798
    GstXWindow * xwindow, const gchar * media_title)
799
800
801
802
803
{
  if (media_title) {
    g_free (ximagesink->media_title);
    ximagesink->media_title = g_strdup (media_title);
  }
804
  if (xwindow) {
805
    /* we have a window */
806
    if (xwindow->internal) {
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
      XTextProperty xproperty;
      const gchar *app_name;
      const gchar *title = NULL;
      gchar *title_mem = NULL;

      /* set application name as a title */
      app_name = g_get_application_name ();

      if (app_name && ximagesink->media_title) {
        title = title_mem = g_strconcat (ximagesink->media_title, " : ",
            app_name, NULL);
      } else if (app_name) {
        title = app_name;
      } else if (ximagesink->media_title) {
        title = ximagesink->media_title;
      }

      if (title) {
        if ((XStringListToTextProperty (((char **) &title), 1,
826
                    &xproperty)) != 0) {
827
          XSetWMName (ximagesink->xcontext->disp, xwindow->win, &xproperty);
828
829
          XFree (xproperty.value);
        }
830
831
832
833
834
835
836

        g_free (title_mem);
      }
    }
  }
}

Julien Moutte's avatar
Julien Moutte committed
837
/* This function handles a GstXWindow creation */
Julien Moutte's avatar
Julien Moutte committed
838
static GstXWindow *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
839
gst_ximagesink_xwindow_new (GstXImageSink * ximagesink, gint width, gint height)
Julien Moutte's avatar
Julien Moutte committed
840
841
{
  GstXWindow *xwindow = NULL;
842
  XGCValues values;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
843

Julien Moutte's avatar
Julien Moutte committed
844
  g_return_val_if_fail (GST_IS_XIMAGESINK (ximagesink), NULL);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
845

Julien Moutte's avatar
Julien Moutte committed
846
  xwindow = g_new0 (GstXWindow, 1);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
847

Julien Moutte's avatar
Julien Moutte committed
848
849
  xwindow->width = width;
  xwindow->height = height;
850
  xwindow->internal = TRUE;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
851

Julien Moutte's avatar
Julien Moutte committed
852
  g_mutex_lock (ximagesink->x_lock);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
853

854
  xwindow->win = XCreateSimpleWindow (ximagesink->xcontext->disp,
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
855
856
      ximagesink->xcontext->root,
      0, 0, xwindow->width, xwindow->height, 0, 0, ximagesink->xcontext->black);
857

858
859
860
861
  /* We have to do that to prevent X from redrawing the background on 
     ConfigureNotify. This takes away flickering of video when resizing. */
  XSetWindowBackgroundPixmap (ximagesink->xcontext->disp, xwindow->win, None);

862
  /* set application name as a title */
863
  gst_ximagesink_xwindow_set_title (ximagesink, xwindow, NULL);
864

865
  if (ximagesink->handle_events) {
866
867
    Atom wm_delete;

868
869
870
    XSelectInput (ximagesink->xcontext->disp, xwindow->win, ExposureMask |
        StructureNotifyMask | PointerMotionMask | KeyPressMask |
        KeyReleaseMask | ButtonPressMask | ButtonReleaseMask);
871
872
873
874
875
876
877

    /* Tell the window manager we'd like delete client messages instead of
     * being killed */
    wm_delete = XInternAtom (ximagesink->xcontext->disp,
        "WM_DELETE_WINDOW", False);
    (void) XSetWMProtocols (ximagesink->xcontext->disp, xwindow->win,
        &wm_delete, 1);
878
  }
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
879

880
881
  xwindow->gc = XCreateGC (ximagesink->xcontext->disp, xwindow->win,
      0, &values);
882

883
  XMapRaised (ximagesink->xcontext->disp, xwindow->win);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
884

885
  XSync (ximagesink->xcontext->disp, FALSE);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
886

Julien Moutte's avatar
Julien Moutte committed
887
  g_mutex_unlock (ximagesink->x_lock);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
888

889
890
  gst_ximagesink_xwindow_decorate (ximagesink, xwindow);

891
  gst_x_overlay_got_window_handle (GST_X_OVERLAY (ximagesink), xwindow->win);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
892

Julien Moutte's avatar
Julien Moutte committed
893
894
895
  return xwindow;
}

Julien Moutte's avatar
Julien Moutte committed
896
/* This function destroys a GstXWindow */
Julien Moutte's avatar
Julien Moutte committed
897
static void
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
898
899
gst_ximagesink_xwindow_destroy (GstXImageSink * ximagesink,
    GstXWindow * xwindow)
Julien Moutte's avatar
Julien Moutte committed
900
901
902
{
  g_return_if_fail (xwindow != NULL);
  g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
903

Julien Moutte's avatar
Julien Moutte committed
904
  g_mutex_lock (ximagesink->x_lock);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
905

Julien Moutte's avatar
Julien Moutte committed
906
  /* If we did not create that window we just free the GC and let it live */
907
  if (xwindow->internal)
Julien Moutte's avatar
Julien Moutte committed
908
    XDestroyWindow (ximagesink->xcontext->disp, xwindow->win);
909
  else
910
    XSelectInput (ximagesink->xcontext->disp, xwindow->win, 0);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
911

Julien Moutte's avatar
Julien Moutte committed
912
  XFreeGC (ximagesink->xcontext->disp, xwindow->gc);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
913

914
  XSync (ximagesink->xcontext->disp, FALSE);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
915

Julien Moutte's avatar
Julien Moutte committed
916
  g_mutex_unlock (ximagesink->x_lock);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
917

Julien Moutte's avatar
Julien Moutte committed
918
919
920
  g_free (xwindow);
}

921
static void
922
gst_ximagesink_xwindow_update_geometry (GstXImageSink * ximagesink)
923
{
924
925
  XWindowAttributes attr;

926
  g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
927

928
  /* Update the window geometry */
929
  g_mutex_lock (ximagesink->x_lock);
930
931
932
933
  if (G_UNLIKELY (ximagesink->xwindow == NULL)) {
    g_mutex_unlock (ximagesink->x_lock);
    return;
  }
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
934

935
936
  XGetWindowAttributes (ximagesink->xcontext->disp,
      ximagesink->xwindow->win, &attr);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
937

938
939
  ximagesink->xwindow->width = attr.width;
  ximagesink->xwindow->height = attr.height;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
940

941
942
  g_mutex_unlock (ximagesink->x_lock);
}
943

944
static void
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
945
gst_ximagesink_xwindow_clear (GstXImageSink * ximagesink, GstXWindow * xwindow)
946
947
948
{
  g_return_if_fail (xwindow != NULL);
  g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
949

950
  g_mutex_lock (ximagesink->x_lock);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
951

952
  XSetForeground (ximagesink->xcontext->disp, xwindow->gc,
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
953
954
      ximagesink->xcontext->black);

955
  XFillRectangle (ximagesink->xcontext->disp, xwindow->win, xwindow->gc,
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
956
957
      0, 0, xwindow->width, xwindow->height);

958
  XSync (ximagesink->xcontext->disp, FALSE);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
959

960
961
962
  g_mutex_unlock (ximagesink->x_lock);
}

Julien Moutte's avatar
Julien Moutte committed
963
964
/* This function handles XEvents that might be in the queue. It generates
   GstEvent that will be sent upstream in the pipeline to handle interactivity
965
   and navigation.*/
966
static void
967
gst_ximagesink_handle_xevents (GstXImag