tizen 2.0 init
[framework/multimedia/gst-plugins-base0.10.git] / sys / ximage / ximagesink.c
1 /* GStreamer
2  * Copyright (C) <2005> Julien Moutte <julien@moutte.net>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 /**
21  * SECTION:element-ximagesink
22  *
23  * XImageSink renders video frames to a drawable (XWindow) on a local or remote
24  * display. This element can receive a Window ID from the application through
25  * the XOverlay interface and will then render video frames in this drawable.
26  * If no Window ID was provided by the application, the element will create its
27  * own internal window and render into it.
28  *
29  * <refsect2>
30  * <title>Scaling</title>
31  * <para>
32  * As standard XImage rendering to a drawable is not scaled, XImageSink will use
33  * reverse caps negotiation to try to get scaled video frames for the drawable.
34  * This is accomplished by asking the peer pad if it accepts some different caps
35  * which in most cases implies that there is a scaling element in the pipeline,
36  * or that an element generating the video frames can generate them with a 
37  * different geometry. This mechanism is handled during buffer allocations, for
38  * each allocation request the video sink will check the drawable geometry, look
39  * at the #GstXImageSink:force-aspect-ratio property, calculate the geometry of
40  * desired video frames and then check that the peer pad accept those new caps.
41  * If it does it will then allocate a buffer in video memory with this new
42  * geometry and return it with the new caps.
43  * </para>
44  * </refsect2>
45  * <refsect2>
46  * <title>Events</title>
47  * <para>
48  * XImageSink creates a thread to handle events coming from the drawable. There
49  * are several kind of events that can be grouped in 2 big categories: input 
50  * events and window state related events. Input events will be translated to
51  * navigation events and pushed upstream for other elements to react on them.
52  * This includes events such as pointer moves, key press/release, clicks etc...
53  * Other events are used to handle the drawable appearance even when the data
54  * is not flowing (GST_STATE_PAUSED). That means that even when the element is
55  * paused, it will receive expose events from the drawable and draw the latest
56  * frame with correct borders/aspect-ratio.
57  * </para>
58  * </refsect2>
59  * <refsect2>
60  * <title>Pixel aspect ratio</title>
61  * <para>
62  * 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  * display if nothing specified. Once this connection is open it will inspect 
65  * the display configuration including the physical display geometry and 
66  * then calculate the pixel aspect ratio. When caps negotiation will occur, the
67  * video sink will set the calculated pixel aspect ratio on the caps to make 
68  * sure that incoming video frames will have the correct pixel aspect ratio for
69  * this display. Sometimes the calculated pixel aspect ratio can be wrong, it is
70  * then possible to enforce a specific pixel aspect ratio using the
71  * #GstXImageSink:pixel-aspect-ratio property.
72  * </para>
73  * </refsect2>
74  * <refsect2>
75  * <title>Examples</title>
76  * |[
77  * gst-launch -v videotestsrc ! queue ! ximagesink
78  * ]| A pipeline to test reverse negotiation. When the test video signal appears
79  * you can resize the window and see that scaled buffers of the desired size are
80  * going to arrive with a short delay. This illustrates how buffers of desired
81  * size are allocated along the way. If you take away the queue, scaling will
82  * happen almost immediately.
83  * |[
84  * gst-launch -v videotestsrc ! navigationtest ! ffmpegcolorspace ! ximagesink
85  * ]| A pipeline to test navigation events.
86  * While moving the mouse pointer over the test signal you will see a black box
87  * following the mouse pointer. If you press the mouse button somewhere on the 
88  * video and release it somewhere else a green box will appear where you pressed
89  * the button and a red one where you released it. (The navigationtest element
90  * 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  * videotestsrc, in most cases the pixel aspect ratio of the display will be
95  * 1/1. This means that videoscale will have to do the scaling to convert 
96  * incoming frames to a size that will match the display pixel aspect ratio
97  * (from 320x240 to 320x180 in this case). Note that you might have to escape 
98  * some characters for your shell like '\(fraction\)'.
99  * </refsect2>
100  */
101
102 #ifdef HAVE_CONFIG_H
103 #include "config.h"
104 #endif
105
106 /* Our interfaces */
107 #include <gst/interfaces/navigation.h>
108 #include <gst/interfaces/xoverlay.h>
109
110 /* Object header */
111 #include "ximagesink.h"
112
113 /* Debugging category */
114 #include <gst/gstinfo.h>
115
116 #include "gst/glib-compat-private.h"
117
118 GST_DEBUG_CATEGORY_EXTERN (gst_debug_ximagesink);
119 #define GST_CAT_DEFAULT gst_debug_ximagesink
120
121 typedef struct
122 {
123   unsigned long flags;
124   unsigned long functions;
125   unsigned long decorations;
126   long input_mode;
127   unsigned long status;
128 }
129 MotifWmHints, MwmHints;
130
131 #define MWM_HINTS_DECORATIONS   (1L << 1)
132
133 static void gst_ximagesink_reset (GstXImageSink * ximagesink);
134 static void gst_ximagesink_ximage_destroy (GstXImageSink * ximagesink,
135     GstXImageBuffer * ximage);
136 static void gst_ximagesink_xwindow_update_geometry (GstXImageSink * ximagesink);
137 static void gst_ximagesink_expose (GstXOverlay * overlay);
138
139 static GstStaticPadTemplate gst_ximagesink_sink_template_factory =
140 GST_STATIC_PAD_TEMPLATE ("sink",
141     GST_PAD_SINK,
142     GST_PAD_ALWAYS,
143     GST_STATIC_CAPS ("video/x-raw-rgb, "
144         "framerate = (fraction) [ 0, MAX ], "
145         "width = (int) [ 1, MAX ], " "height = (int) [ 1, MAX ]")
146     );
147
148 enum
149 {
150   PROP_0,
151   PROP_DISPLAY,
152   PROP_SYNCHRONOUS,
153   PROP_PIXEL_ASPECT_RATIO,
154   PROP_FORCE_ASPECT_RATIO,
155   PROP_HANDLE_EVENTS,
156   PROP_HANDLE_EXPOSE,
157   PROP_WINDOW_WIDTH,
158   PROP_WINDOW_HEIGHT
159 };
160
161 static GstVideoSinkClass *parent_class = NULL;
162
163 /* ============================================================= */
164 /*                                                               */
165 /*                       Private Methods                         */
166 /*                                                               */
167 /* ============================================================= */
168
169 /* ximage buffers */
170
171 static GstBufferClass *ximage_buffer_parent_class = NULL;
172
173 #define GST_TYPE_XIMAGE_BUFFER (gst_ximage_buffer_get_type())
174
175 #define GST_IS_XIMAGE_BUFFER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_XIMAGE_BUFFER))
176 #define GST_XIMAGE_BUFFER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_XIMAGE_BUFFER, GstXImageBuffer))
177 #define GST_XIMAGE_BUFFER_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_XIMAGE_BUFFER, GstXImageBufferClass))
178
179 /* So some words about GstMiniObject, this is pretty messy...
180    GstMiniObject does not use the standard finalizing of GObjects, you are 
181    supposed to call gst_buffer_unref that's going to call gst_mini_objec_unref
182    which will handle its own refcount system and call gst_mini_object_free.
183    gst_mini_object_free will call the class finalize method which is not the 
184    one from GObject, after calling this finalize method it will free the object
185    instance for you if the refcount is still 0 so you should not chain up */
186 static void
187 gst_ximage_buffer_finalize (GstXImageBuffer * ximage)
188 {
189   GstXImageSink *ximagesink = NULL;
190   gboolean recycled = FALSE;
191   gboolean running;
192
193   g_return_if_fail (ximage != NULL);
194
195   ximagesink = ximage->ximagesink;
196   if (G_UNLIKELY (ximagesink == NULL)) {
197     GST_WARNING_OBJECT (ximagesink, "no sink found");
198     goto beach;
199   }
200
201   GST_OBJECT_LOCK (ximagesink);
202   running = ximagesink->running;
203   GST_OBJECT_UNLOCK (ximagesink);
204
205   if (running == FALSE) {
206     /* If the sink is shutting down, need to clear the image */
207     GST_DEBUG_OBJECT (ximagesink,
208         "destroy image %p because the sink is shutting down", ximage);
209     gst_ximagesink_ximage_destroy (ximagesink, ximage);
210   } else if ((ximage->width != GST_VIDEO_SINK_WIDTH (ximagesink)) ||
211       (ximage->height != GST_VIDEO_SINK_HEIGHT (ximagesink))) {
212     /* If our geometry changed we can't reuse that image. */
213     GST_DEBUG_OBJECT (ximagesink,
214         "destroy image %p as its size changed %dx%d vs current %dx%d",
215         ximage, ximage->width, ximage->height,
216         GST_VIDEO_SINK_WIDTH (ximagesink), GST_VIDEO_SINK_HEIGHT (ximagesink));
217     gst_ximagesink_ximage_destroy (ximagesink, ximage);
218   } else {
219     /* In that case we can reuse the image and add it to our image pool. */
220     GST_LOG_OBJECT (ximagesink, "recycling image %p in pool", ximage);
221     /* need to increment the refcount again to recycle */
222     gst_buffer_ref (GST_BUFFER_CAST (ximage));
223     g_mutex_lock (ximagesink->pool_lock);
224     ximagesink->buffer_pool = g_slist_prepend (ximagesink->buffer_pool, ximage);
225     g_mutex_unlock (ximagesink->pool_lock);
226     recycled = TRUE;
227   }
228
229   if (!recycled)
230     GST_MINI_OBJECT_CLASS (ximage_buffer_parent_class)->finalize
231         (GST_MINI_OBJECT (ximage));
232
233 beach:
234   return;
235 }
236
237 static void
238 gst_ximage_buffer_free (GstXImageBuffer * ximage)
239 {
240   /* make sure it is not recycled */
241   ximage->width = -1;
242   ximage->height = -1;
243   gst_buffer_unref (GST_BUFFER_CAST (ximage));
244 }
245
246 static void
247 gst_ximage_buffer_init (GstXImageBuffer * ximage_buffer, gpointer g_class)
248 {
249 #ifdef HAVE_XSHM
250   ximage_buffer->SHMInfo.shmaddr = ((void *) -1);
251   ximage_buffer->SHMInfo.shmid = -1;
252 #endif
253 }
254
255 static void
256 gst_ximage_buffer_class_init (gpointer g_class, gpointer class_data)
257 {
258   GstMiniObjectClass *mini_object_class = GST_MINI_OBJECT_CLASS (g_class);
259
260   ximage_buffer_parent_class = g_type_class_peek_parent (g_class);
261
262   mini_object_class->finalize = (GstMiniObjectFinalizeFunction)
263       gst_ximage_buffer_finalize;
264 }
265
266 static GType
267 gst_ximage_buffer_get_type (void)
268 {
269   static GType _gst_ximage_buffer_type;
270
271   if (G_UNLIKELY (_gst_ximage_buffer_type == 0)) {
272     static const GTypeInfo ximage_buffer_info = {
273       sizeof (GstBufferClass),
274       NULL,
275       NULL,
276       gst_ximage_buffer_class_init,
277       NULL,
278       NULL,
279       sizeof (GstXImageBuffer),
280       0,
281       (GInstanceInitFunc) gst_ximage_buffer_init,
282       NULL
283     };
284     _gst_ximage_buffer_type = g_type_register_static (GST_TYPE_BUFFER,
285         "GstXImageBuffer", &ximage_buffer_info, 0);
286   }
287   return _gst_ximage_buffer_type;
288 }
289
290 /* X11 stuff */
291
292 static gboolean error_caught = FALSE;
293
294 static int
295 gst_ximagesink_handle_xerror (Display * display, XErrorEvent * xevent)
296 {
297   char error_msg[1024];
298
299   XGetErrorText (display, xevent->error_code, error_msg, 1024);
300   GST_DEBUG ("ximagesink triggered an XError. error: %s", error_msg);
301   error_caught = TRUE;
302   return 0;
303 }
304
305 #ifdef HAVE_XSHM                /* Check that XShm calls actually work */
306
307 static gboolean
308 gst_ximagesink_check_xshm_calls (GstXImageSink * ximagesink,
309     GstXContext * xcontext)
310 {
311   XImage *ximage;
312   XShmSegmentInfo SHMInfo;
313   size_t size;
314   int (*handler) (Display *, XErrorEvent *);
315   gboolean result = FALSE;
316   gboolean did_attach = FALSE;
317
318   g_return_val_if_fail (xcontext != NULL, FALSE);
319
320   /* Sync to ensure any older errors are already processed */
321   XSync (xcontext->disp, FALSE);
322
323   /* Set defaults so we don't free these later unnecessarily */
324   SHMInfo.shmaddr = ((void *) -1);
325   SHMInfo.shmid = -1;
326
327   /* Setting an error handler to catch failure */
328   error_caught = FALSE;
329   handler = XSetErrorHandler (gst_ximagesink_handle_xerror);
330
331   /* Trying to create a 1x1 ximage */
332   GST_DEBUG ("XShmCreateImage of 1x1");
333
334   ximage = XShmCreateImage (xcontext->disp, xcontext->visual,
335       xcontext->depth, ZPixmap, NULL, &SHMInfo, 1, 1);
336
337   /* Might cause an error, sync to ensure it is noticed */
338   XSync (xcontext->disp, FALSE);
339   if (!ximage || error_caught) {
340     GST_WARNING ("could not XShmCreateImage a 1x1 image");
341     goto beach;
342   }
343   size = ximage->height * ximage->bytes_per_line;
344
345   SHMInfo.shmid = shmget (IPC_PRIVATE, size, IPC_CREAT | 0777);
346   if (SHMInfo.shmid == -1) {
347     GST_WARNING ("could not get shared memory of %" G_GSIZE_FORMAT " bytes",
348         size);
349     goto beach;
350   }
351
352   SHMInfo.shmaddr = shmat (SHMInfo.shmid, NULL, 0);
353   if (SHMInfo.shmaddr == ((void *) -1)) {
354     GST_WARNING ("Failed to shmat: %s", g_strerror (errno));
355     /* Clean up shm seg */
356     shmctl (SHMInfo.shmid, IPC_RMID, NULL);
357     goto beach;
358   }
359
360   ximage->data = SHMInfo.shmaddr;
361   SHMInfo.readOnly = FALSE;
362
363   if (XShmAttach (xcontext->disp, &SHMInfo) == 0) {
364     GST_WARNING ("Failed to XShmAttach");
365     /* Clean up shm seg */
366     shmctl (SHMInfo.shmid, IPC_RMID, NULL);
367     goto beach;
368   }
369
370   /* Sync to ensure we see any errors we caused */
371   XSync (xcontext->disp, FALSE);
372
373   /* Delete the shared memory segment as soon as everyone is attached. 
374    * This way, it will be deleted as soon as we detach later, and not
375    * leaked if we crash. */
376   shmctl (SHMInfo.shmid, IPC_RMID, NULL);
377
378   if (!error_caught) {
379     did_attach = TRUE;
380     /* store whether we succeeded in result */
381     result = TRUE;
382   }
383
384 beach:
385   /* Sync to ensure we swallow any errors we caused and reset error_caught */
386   XSync (xcontext->disp, FALSE);
387   error_caught = FALSE;
388   XSetErrorHandler (handler);
389
390   if (did_attach) {
391     XShmDetach (xcontext->disp, &SHMInfo);
392     XSync (xcontext->disp, FALSE);
393   }
394   if (SHMInfo.shmaddr != ((void *) -1))
395     shmdt (SHMInfo.shmaddr);
396   if (ximage)
397     XDestroyImage (ximage);
398   return result;
399 }
400 #endif /* HAVE_XSHM */
401
402 /* This function handles GstXImageBuffer creation depending on XShm availability */
403 static GstXImageBuffer *
404 gst_ximagesink_ximage_new (GstXImageSink * ximagesink, GstCaps * caps)
405 {
406   GstXImageBuffer *ximage = NULL;
407   GstStructure *structure = NULL;
408   gboolean succeeded = FALSE;
409   int (*handler) (Display *, XErrorEvent *);
410
411   g_return_val_if_fail (GST_IS_XIMAGESINK (ximagesink), NULL);
412
413   ximage = (GstXImageBuffer *) gst_mini_object_new (GST_TYPE_XIMAGE_BUFFER);
414
415   structure = gst_caps_get_structure (caps, 0);
416
417   if (!gst_structure_get_int (structure, "width", &ximage->width) ||
418       !gst_structure_get_int (structure, "height", &ximage->height)) {
419     GST_WARNING ("failed getting geometry from caps %" GST_PTR_FORMAT, caps);
420   }
421
422   GST_DEBUG_OBJECT (ximagesink, "creating image %p (%dx%d)", ximage,
423       ximage->width, ximage->height);
424
425   g_mutex_lock (ximagesink->x_lock);
426
427   /* Setting an error handler to catch failure */
428   error_caught = FALSE;
429   handler = XSetErrorHandler (gst_ximagesink_handle_xerror);
430
431 #ifdef HAVE_XSHM
432   if (ximagesink->xcontext->use_xshm) {
433     ximage->ximage = XShmCreateImage (ximagesink->xcontext->disp,
434         ximagesink->xcontext->visual,
435         ximagesink->xcontext->depth,
436         ZPixmap, NULL, &ximage->SHMInfo, ximage->width, ximage->height);
437     if (!ximage->ximage || error_caught) {
438       g_mutex_unlock (ximagesink->x_lock);
439
440       /* Reset error flag */
441       error_caught = FALSE;
442
443       /* Push a warning */
444       GST_ELEMENT_WARNING (ximagesink, RESOURCE, WRITE,
445           ("Failed to create output image buffer of %dx%d pixels",
446               ximage->width, ximage->height),
447           ("could not XShmCreateImage a %dx%d image",
448               ximage->width, ximage->height));
449
450       /* Retry without XShm */
451       ximagesink->xcontext->use_xshm = FALSE;
452
453       /* Hold X mutex again to try without XShm */
454       g_mutex_lock (ximagesink->x_lock);
455       goto no_xshm;
456     }
457
458     /* we have to use the returned bytes_per_line for our shm size */
459     ximage->size = ximage->ximage->bytes_per_line * ximage->ximage->height;
460     GST_LOG_OBJECT (ximagesink,
461         "XShm image size is %" G_GSIZE_FORMAT ", width %d, stride %d",
462         ximage->size, ximage->width, ximage->ximage->bytes_per_line);
463
464     ximage->SHMInfo.shmid = shmget (IPC_PRIVATE, ximage->size,
465         IPC_CREAT | 0777);
466     if (ximage->SHMInfo.shmid == -1) {
467       g_mutex_unlock (ximagesink->x_lock);
468       GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
469           ("Failed to create output image buffer of %dx%d pixels",
470               ximage->width, ximage->height),
471           ("could not get shared memory of %" G_GSIZE_FORMAT " bytes",
472               ximage->size));
473       goto beach;
474     }
475
476     ximage->SHMInfo.shmaddr = shmat (ximage->SHMInfo.shmid, NULL, 0);
477     if (ximage->SHMInfo.shmaddr == ((void *) -1)) {
478       g_mutex_unlock (ximagesink->x_lock);
479       GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
480           ("Failed to create output image buffer of %dx%d pixels",
481               ximage->width, ximage->height),
482           ("Failed to shmat: %s", g_strerror (errno)));
483       /* Clean up the shared memory segment */
484       shmctl (ximage->SHMInfo.shmid, IPC_RMID, NULL);
485       goto beach;
486     }
487
488     ximage->ximage->data = ximage->SHMInfo.shmaddr;
489     ximage->SHMInfo.readOnly = FALSE;
490
491     if (XShmAttach (ximagesink->xcontext->disp, &ximage->SHMInfo) == 0) {
492       /* Clean up shm seg */
493       shmctl (ximage->SHMInfo.shmid, IPC_RMID, NULL);
494
495       g_mutex_unlock (ximagesink->x_lock);
496       GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
497           ("Failed to create output image buffer of %dx%d pixels",
498               ximage->width, ximage->height), ("Failed to XShmAttach"));
499       goto beach;
500     }
501
502     XSync (ximagesink->xcontext->disp, FALSE);
503
504     /* Now that everyone has attached, we can delete the shared memory segment.
505      * This way, it will be deleted as soon as we detach later, and not
506      * leaked if we crash. */
507     shmctl (ximage->SHMInfo.shmid, IPC_RMID, NULL);
508
509   } else
510   no_xshm:
511 #endif /* HAVE_XSHM */
512   {
513     guint allocsize;
514
515     ximage->ximage = XCreateImage (ximagesink->xcontext->disp,
516         ximagesink->xcontext->visual,
517         ximagesink->xcontext->depth,
518         ZPixmap, 0, NULL,
519         ximage->width, ximage->height, ximagesink->xcontext->bpp, 0);
520     if (!ximage->ximage || error_caught) {
521       g_mutex_unlock (ximagesink->x_lock);
522       /* Reset error handler */
523       error_caught = FALSE;
524       XSetErrorHandler (handler);
525       /* Push an error */
526       GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
527           ("Failed to create output image buffer of %dx%d pixels",
528               ximage->width, ximage->height),
529           ("could not XCreateImage a %dx%d image",
530               ximage->width, ximage->height));
531       goto beach;
532     }
533
534     /* upstream will assume that rowstrides are multiples of 4, but this
535      * doesn't always seem to be the case with XCreateImage() */
536     if ((ximage->ximage->bytes_per_line % 4) != 0) {
537       GST_WARNING_OBJECT (ximagesink, "returned stride not a multiple of 4 as "
538           "usually assumed");
539     }
540
541     /* we have to use the returned bytes_per_line for our image size */
542     ximage->size = ximage->ximage->bytes_per_line * ximage->ximage->height;
543
544     /* alloc a bit more for unexpected strides to avoid crashes upstream.
545      * FIXME: if we get an unrounded stride, the image will be displayed
546      * distorted, since all upstream elements assume a rounded stride */
547     allocsize =
548         GST_ROUND_UP_4 (ximage->ximage->bytes_per_line) *
549         ximage->ximage->height;
550     ximage->ximage->data = g_malloc (allocsize);
551     GST_LOG_OBJECT (ximagesink,
552         "non-XShm image size is %" G_GSIZE_FORMAT " (alloced: %u), width %d, "
553         "stride %d", ximage->size, allocsize, ximage->width,
554         ximage->ximage->bytes_per_line);
555
556     XSync (ximagesink->xcontext->disp, FALSE);
557   }
558
559   /* Reset error handler */
560   error_caught = FALSE;
561   XSetErrorHandler (handler);
562
563   succeeded = TRUE;
564
565   GST_BUFFER_DATA (ximage) = (guchar *) ximage->ximage->data;
566   GST_BUFFER_SIZE (ximage) = ximage->size;
567
568   /* Keep a ref to our sink */
569   ximage->ximagesink = gst_object_ref (ximagesink);
570
571   g_mutex_unlock (ximagesink->x_lock);
572 beach:
573   if (!succeeded) {
574     gst_ximage_buffer_free (ximage);
575     ximage = NULL;
576   }
577
578   return ximage;
579 }
580
581 /* This function destroys a GstXImageBuffer handling XShm availability */
582 static void
583 gst_ximagesink_ximage_destroy (GstXImageSink * ximagesink,
584     GstXImageBuffer * ximage)
585 {
586   g_return_if_fail (ximage != NULL);
587   g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
588
589   /* Hold the object lock to ensure the XContext doesn't disappear */
590   GST_OBJECT_LOCK (ximagesink);
591
592   /* If the destroyed image is the current one we destroy our reference too */
593   if (ximagesink->cur_image == ximage) {
594     ximagesink->cur_image = NULL;
595   }
596
597   /* We might have some buffers destroyed after changing state to NULL */
598   if (!ximagesink->xcontext) {
599     GST_DEBUG_OBJECT (ximagesink, "Destroying XImage after XContext");
600 #ifdef HAVE_XSHM
601     if (ximage->SHMInfo.shmaddr != ((void *) -1)) {
602       shmdt (ximage->SHMInfo.shmaddr);
603     }
604 #endif
605     goto beach;
606   }
607
608   g_mutex_lock (ximagesink->x_lock);
609
610 #ifdef HAVE_XSHM
611   if (ximagesink->xcontext->use_xshm) {
612     if (ximage->SHMInfo.shmaddr != ((void *) -1)) {
613       XShmDetach (ximagesink->xcontext->disp, &ximage->SHMInfo);
614       XSync (ximagesink->xcontext->disp, 0);
615       shmdt (ximage->SHMInfo.shmaddr);
616     }
617     if (ximage->ximage)
618       XDestroyImage (ximage->ximage);
619
620   } else
621 #endif /* HAVE_XSHM */
622   {
623     if (ximage->ximage) {
624       XDestroyImage (ximage->ximage);
625     }
626   }
627
628   XSync (ximagesink->xcontext->disp, FALSE);
629
630   g_mutex_unlock (ximagesink->x_lock);
631
632 beach:
633   GST_OBJECT_UNLOCK (ximagesink);
634
635   if (ximage->ximagesink) {
636     /* Release the ref to our sink */
637     ximage->ximagesink = NULL;
638     gst_object_unref (ximagesink);
639   }
640
641   return;
642 }
643
644 /* We are called with the x_lock taken */
645 static void
646 gst_ximagesink_xwindow_draw_borders (GstXImageSink * ximagesink,
647     GstXWindow * xwindow, GstVideoRectangle rect)
648 {
649   g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
650   g_return_if_fail (xwindow != NULL);
651
652   XSetForeground (ximagesink->xcontext->disp, xwindow->gc,
653       ximagesink->xcontext->black);
654
655   /* Left border */
656   if (rect.x > 0) {
657     XFillRectangle (ximagesink->xcontext->disp, xwindow->win, xwindow->gc,
658         0, 0, rect.x, xwindow->height);
659   }
660
661   /* Right border */
662   if ((rect.x + rect.w) < xwindow->width) {
663     XFillRectangle (ximagesink->xcontext->disp, xwindow->win, xwindow->gc,
664         rect.x + rect.w, 0, xwindow->width, xwindow->height);
665   }
666
667   /* Top border */
668   if (rect.y > 0) {
669     XFillRectangle (ximagesink->xcontext->disp, xwindow->win, xwindow->gc,
670         0, 0, xwindow->width, rect.y);
671   }
672
673   /* Bottom border */
674   if ((rect.y + rect.h) < xwindow->height) {
675     XFillRectangle (ximagesink->xcontext->disp, xwindow->win, xwindow->gc,
676         0, rect.y + rect.h, xwindow->width, xwindow->height);
677   }
678 }
679
680 /* This function puts a GstXImageBuffer on a GstXImageSink's window */
681 static gboolean
682 gst_ximagesink_ximage_put (GstXImageSink * ximagesink, GstXImageBuffer * ximage)
683 {
684   GstVideoRectangle src, dst, result;
685   gboolean draw_border = FALSE;
686
687   g_return_val_if_fail (GST_IS_XIMAGESINK (ximagesink), FALSE);
688
689   /* We take the flow_lock. If expose is in there we don't want to run
690      concurrently from the data flow thread */
691   g_mutex_lock (ximagesink->flow_lock);
692
693   if (G_UNLIKELY (ximagesink->xwindow == NULL)) {
694     g_mutex_unlock (ximagesink->flow_lock);
695     return FALSE;
696   }
697
698   /* Draw borders when displaying the first frame. After this
699      draw borders only on expose event or caps change (ximagesink->draw_border = TRUE). */
700   if (!ximagesink->cur_image || ximagesink->draw_border) {
701     draw_border = TRUE;
702   }
703
704   /* Store a reference to the last image we put, lose the previous one */
705   if (ximage && ximagesink->cur_image != ximage) {
706     if (ximagesink->cur_image) {
707       GST_LOG_OBJECT (ximagesink, "unreffing %p", ximagesink->cur_image);
708       gst_buffer_unref (GST_BUFFER_CAST (ximagesink->cur_image));
709     }
710     GST_LOG_OBJECT (ximagesink, "reffing %p as our current image", ximage);
711     ximagesink->cur_image =
712         GST_XIMAGE_BUFFER (gst_buffer_ref (GST_BUFFER_CAST (ximage)));
713   }
714
715   /* Expose sends a NULL image, we take the latest frame */
716   if (!ximage) {
717     draw_border = TRUE;
718     if (ximagesink->cur_image) {
719       ximage = ximagesink->cur_image;
720     } else {
721       g_mutex_unlock (ximagesink->flow_lock);
722       return TRUE;
723     }
724   }
725
726   src.w = ximage->width;
727   src.h = ximage->height;
728   dst.w = ximagesink->xwindow->width;
729   dst.h = ximagesink->xwindow->height;
730
731   gst_video_sink_center_rect (src, dst, &result, FALSE);
732
733   g_mutex_lock (ximagesink->x_lock);
734
735   if (draw_border) {
736     gst_ximagesink_xwindow_draw_borders (ximagesink, ximagesink->xwindow,
737         result);
738     ximagesink->draw_border = FALSE;
739   }
740 #ifdef HAVE_XSHM
741   if (ximagesink->xcontext->use_xshm) {
742     GST_LOG_OBJECT (ximagesink,
743         "XShmPutImage on %p, src: %d, %d - dest: %d, %d, dim: %dx%d, win %dx%d",
744         ximage, 0, 0, result.x, result.y, result.w, result.h,
745         ximagesink->xwindow->width, ximagesink->xwindow->height);
746     XShmPutImage (ximagesink->xcontext->disp, ximagesink->xwindow->win,
747         ximagesink->xwindow->gc, ximage->ximage, 0, 0, result.x, result.y,
748         result.w, result.h, FALSE);
749   } else
750 #endif /* HAVE_XSHM */
751   {
752     GST_LOG_OBJECT (ximagesink,
753         "XPutImage on %p, src: %d, %d - dest: %d, %d, dim: %dx%d, win %dx%d",
754         ximage, 0, 0, result.x, result.y, result.w, result.h,
755         ximagesink->xwindow->width, ximagesink->xwindow->height);
756     XPutImage (ximagesink->xcontext->disp, ximagesink->xwindow->win,
757         ximagesink->xwindow->gc, ximage->ximage, 0, 0, result.x, result.y,
758         result.w, result.h);
759   }
760
761   XSync (ximagesink->xcontext->disp, FALSE);
762
763   g_mutex_unlock (ximagesink->x_lock);
764
765   g_mutex_unlock (ximagesink->flow_lock);
766
767   return TRUE;
768 }
769
770 static gboolean
771 gst_ximagesink_xwindow_decorate (GstXImageSink * ximagesink,
772     GstXWindow * window)
773 {
774   Atom hints_atom = None;
775   MotifWmHints *hints;
776
777   g_return_val_if_fail (GST_IS_XIMAGESINK (ximagesink), FALSE);
778   g_return_val_if_fail (window != NULL, FALSE);
779
780   g_mutex_lock (ximagesink->x_lock);
781
782   hints_atom = XInternAtom (ximagesink->xcontext->disp, "_MOTIF_WM_HINTS", 1);
783   if (hints_atom == None) {
784     g_mutex_unlock (ximagesink->x_lock);
785     return FALSE;
786   }
787
788   hints = g_malloc0 (sizeof (MotifWmHints));
789
790   hints->flags |= MWM_HINTS_DECORATIONS;
791   hints->decorations = 1 << 0;
792
793   XChangeProperty (ximagesink->xcontext->disp, window->win,
794       hints_atom, hints_atom, 32, PropModeReplace,
795       (guchar *) hints, sizeof (MotifWmHints) / sizeof (long));
796
797   XSync (ximagesink->xcontext->disp, FALSE);
798
799   g_mutex_unlock (ximagesink->x_lock);
800
801   g_free (hints);
802
803   return TRUE;
804 }
805
806 static void
807 gst_ximagesink_xwindow_set_title (GstXImageSink * ximagesink,
808     GstXWindow * xwindow, const gchar * media_title)
809 {
810   if (media_title) {
811     g_free (ximagesink->media_title);
812     ximagesink->media_title = g_strdup (media_title);
813   }
814   if (xwindow) {
815     /* we have a window */
816     if (xwindow->internal) {
817       XTextProperty xproperty;
818       const gchar *app_name;
819       const gchar *title = NULL;
820       gchar *title_mem = NULL;
821
822       /* set application name as a title */
823       app_name = g_get_application_name ();
824
825       if (app_name && ximagesink->media_title) {
826         title = title_mem = g_strconcat (ximagesink->media_title, " : ",
827             app_name, NULL);
828       } else if (app_name) {
829         title = app_name;
830       } else if (ximagesink->media_title) {
831         title = ximagesink->media_title;
832       }
833
834       if (title) {
835         if ((XStringListToTextProperty (((char **) &title), 1,
836                     &xproperty)) != 0) {
837           XSetWMName (ximagesink->xcontext->disp, xwindow->win, &xproperty);
838           XFree (xproperty.value);
839         }
840
841         g_free (title_mem);
842       }
843     }
844   }
845 }
846
847 /* This function handles a GstXWindow creation */
848 static GstXWindow *
849 gst_ximagesink_xwindow_new (GstXImageSink * ximagesink, gint width, gint height)
850 {
851   GstXWindow *xwindow = NULL;
852   XGCValues values;
853
854   g_return_val_if_fail (GST_IS_XIMAGESINK (ximagesink), NULL);
855
856   xwindow = g_new0 (GstXWindow, 1);
857
858   xwindow->width = width;
859   xwindow->height = height;
860   xwindow->internal = TRUE;
861
862   g_mutex_lock (ximagesink->x_lock);
863
864   xwindow->win = XCreateSimpleWindow (ximagesink->xcontext->disp,
865       ximagesink->xcontext->root,
866       0, 0, xwindow->width, xwindow->height, 0, 0, ximagesink->xcontext->black);
867
868   /* We have to do that to prevent X from redrawing the background on 
869      ConfigureNotify. This takes away flickering of video when resizing. */
870   XSetWindowBackgroundPixmap (ximagesink->xcontext->disp, xwindow->win, None);
871
872   /* set application name as a title */
873   gst_ximagesink_xwindow_set_title (ximagesink, xwindow, NULL);
874
875   if (ximagesink->handle_events) {
876     Atom wm_delete;
877
878     XSelectInput (ximagesink->xcontext->disp, xwindow->win, ExposureMask |
879         StructureNotifyMask | PointerMotionMask | KeyPressMask |
880         KeyReleaseMask | ButtonPressMask | ButtonReleaseMask);
881
882     /* Tell the window manager we'd like delete client messages instead of
883      * being killed */
884     wm_delete = XInternAtom (ximagesink->xcontext->disp,
885         "WM_DELETE_WINDOW", False);
886     (void) XSetWMProtocols (ximagesink->xcontext->disp, xwindow->win,
887         &wm_delete, 1);
888   }
889
890   xwindow->gc = XCreateGC (ximagesink->xcontext->disp, xwindow->win,
891       0, &values);
892
893   XMapRaised (ximagesink->xcontext->disp, xwindow->win);
894
895   XSync (ximagesink->xcontext->disp, FALSE);
896
897   g_mutex_unlock (ximagesink->x_lock);
898
899   gst_ximagesink_xwindow_decorate (ximagesink, xwindow);
900
901   gst_x_overlay_got_window_handle (GST_X_OVERLAY (ximagesink), xwindow->win);
902
903   return xwindow;
904 }
905
906 /* This function destroys a GstXWindow */
907 static void
908 gst_ximagesink_xwindow_destroy (GstXImageSink * ximagesink,
909     GstXWindow * xwindow)
910 {
911   g_return_if_fail (xwindow != NULL);
912   g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
913
914   g_mutex_lock (ximagesink->x_lock);
915
916   /* If we did not create that window we just free the GC and let it live */
917   if (xwindow->internal)
918     XDestroyWindow (ximagesink->xcontext->disp, xwindow->win);
919   else
920     XSelectInput (ximagesink->xcontext->disp, xwindow->win, 0);
921
922   XFreeGC (ximagesink->xcontext->disp, xwindow->gc);
923
924   XSync (ximagesink->xcontext->disp, FALSE);
925
926   g_mutex_unlock (ximagesink->x_lock);
927
928   g_free (xwindow);
929 }
930
931 static void
932 gst_ximagesink_xwindow_update_geometry (GstXImageSink * ximagesink)
933 {
934   XWindowAttributes attr;
935
936   g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
937
938   /* Update the window geometry */
939   g_mutex_lock (ximagesink->x_lock);
940   if (G_UNLIKELY (ximagesink->xwindow == NULL)) {
941     g_mutex_unlock (ximagesink->x_lock);
942     return;
943   }
944
945   XGetWindowAttributes (ximagesink->xcontext->disp,
946       ximagesink->xwindow->win, &attr);
947
948   ximagesink->xwindow->width = attr.width;
949   ximagesink->xwindow->height = attr.height;
950
951   g_mutex_unlock (ximagesink->x_lock);
952 }
953
954 static void
955 gst_ximagesink_xwindow_clear (GstXImageSink * ximagesink, GstXWindow * xwindow)
956 {
957   g_return_if_fail (xwindow != NULL);
958   g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
959
960   g_mutex_lock (ximagesink->x_lock);
961
962   XSetForeground (ximagesink->xcontext->disp, xwindow->gc,
963       ximagesink->xcontext->black);
964
965   XFillRectangle (ximagesink->xcontext->disp, xwindow->win, xwindow->gc,
966       0, 0, xwindow->width, xwindow->height);
967
968   XSync (ximagesink->xcontext->disp, FALSE);
969
970   g_mutex_unlock (ximagesink->x_lock);
971 }
972
973 /* This function handles XEvents that might be in the queue. It generates
974    GstEvent that will be sent upstream in the pipeline to handle interactivity
975    and navigation.*/
976 static void
977 gst_ximagesink_handle_xevents (GstXImageSink * ximagesink)
978 {
979   XEvent e;
980   guint pointer_x = 0, pointer_y = 0;
981   gboolean pointer_moved = FALSE;
982   gboolean exposed = FALSE, configured = FALSE;
983
984   g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
985
986   /* Then we get all pointer motion events, only the last position is
987      interesting. */
988   g_mutex_lock (ximagesink->flow_lock);
989   g_mutex_lock (ximagesink->x_lock);
990   while (XCheckWindowEvent (ximagesink->xcontext->disp,
991           ximagesink->xwindow->win, PointerMotionMask, &e)) {
992     g_mutex_unlock (ximagesink->x_lock);
993     g_mutex_unlock (ximagesink->flow_lock);
994
995     switch (e.type) {
996       case MotionNotify:
997         pointer_x = e.xmotion.x;
998         pointer_y = e.xmotion.y;
999         pointer_moved = TRUE;
1000         break;
1001       default:
1002         break;
1003     }
1004     g_mutex_lock (ximagesink->flow_lock);
1005     g_mutex_lock (ximagesink->x_lock);
1006   }
1007
1008   if (pointer_moved) {
1009     g_mutex_unlock (ximagesink->x_lock);
1010     g_mutex_unlock (ximagesink->flow_lock);
1011
1012     GST_DEBUG ("ximagesink pointer moved over window at %d,%d",
1013         pointer_x, pointer_y);
1014     gst_navigation_send_mouse_event (GST_NAVIGATION (ximagesink),
1015         "mouse-move", 0, pointer_x, pointer_y);
1016
1017     g_mutex_lock (ximagesink->flow_lock);
1018     g_mutex_lock (ximagesink->x_lock);
1019   }
1020
1021   /* We get all remaining events on our window to throw them upstream */
1022   while (XCheckWindowEvent (ximagesink->xcontext->disp,
1023           ximagesink->xwindow->win,
1024           KeyPressMask | KeyReleaseMask |
1025           ButtonPressMask | ButtonReleaseMask, &e)) {
1026     KeySym keysym;
1027
1028     /* We lock only for the X function call */
1029     g_mutex_unlock (ximagesink->x_lock);
1030     g_mutex_unlock (ximagesink->flow_lock);
1031
1032     switch (e.type) {
1033       case ButtonPress:
1034         /* Mouse button pressed/released over our window. We send upstream
1035            events for interactivity/navigation */
1036         GST_DEBUG ("ximagesink button %d pressed over window at %d,%d",
1037             e.xbutton.button, e.xbutton.x, e.xbutton.x);
1038         gst_navigation_send_mouse_event (GST_NAVIGATION (ximagesink),
1039             "mouse-button-press", e.xbutton.button, e.xbutton.x, e.xbutton.y);
1040         break;
1041       case ButtonRelease:
1042         GST_DEBUG ("ximagesink button %d release over window at %d,%d",
1043             e.xbutton.button, e.xbutton.x, e.xbutton.x);
1044         gst_navigation_send_mouse_event (GST_NAVIGATION (ximagesink),
1045             "mouse-button-release", e.xbutton.button, e.xbutton.x, e.xbutton.y);
1046         break;
1047       case KeyPress:
1048       case KeyRelease:
1049         /* Key pressed/released over our window. We send upstream
1050            events for interactivity/navigation */
1051         GST_DEBUG ("ximagesink key %d pressed over window at %d,%d",
1052             e.xkey.keycode, e.xkey.x, e.xkey.x);
1053         g_mutex_lock (ximagesink->x_lock);
1054         keysym = XKeycodeToKeysym (ximagesink->xcontext->disp,
1055             e.xkey.keycode, 0);
1056         g_mutex_unlock (ximagesink->x_lock);
1057         if (keysym != NoSymbol) {
1058           char *key_str = NULL;
1059
1060           g_mutex_lock (ximagesink->x_lock);
1061           key_str = XKeysymToString (keysym);
1062           g_mutex_unlock (ximagesink->x_lock);
1063           gst_navigation_send_key_event (GST_NAVIGATION (ximagesink),
1064               e.type == KeyPress ? "key-press" : "key-release", key_str);
1065
1066         } else {
1067           gst_navigation_send_key_event (GST_NAVIGATION (ximagesink),
1068               e.type == KeyPress ? "key-press" : "key-release", "unknown");
1069         }
1070         break;
1071       default:
1072         GST_DEBUG_OBJECT (ximagesink, "ximagesink unhandled X event (%d)",
1073             e.type);
1074     }
1075     g_mutex_lock (ximagesink->flow_lock);
1076     g_mutex_lock (ximagesink->x_lock);
1077   }
1078
1079   while (XCheckWindowEvent (ximagesink->xcontext->disp,
1080           ximagesink->xwindow->win, ExposureMask | StructureNotifyMask, &e)) {
1081     switch (e.type) {
1082       case Expose:
1083         exposed = TRUE;
1084         break;
1085       case ConfigureNotify:
1086         g_mutex_unlock (ximagesink->x_lock);
1087         gst_ximagesink_xwindow_update_geometry (ximagesink);
1088         g_mutex_lock (ximagesink->x_lock);
1089         configured = TRUE;
1090         break;
1091       default:
1092         break;
1093     }
1094   }
1095
1096   if (ximagesink->handle_expose && (exposed || configured)) {
1097     g_mutex_unlock (ximagesink->x_lock);
1098     g_mutex_unlock (ximagesink->flow_lock);
1099
1100     gst_ximagesink_expose (GST_X_OVERLAY (ximagesink));
1101
1102     g_mutex_lock (ximagesink->flow_lock);
1103     g_mutex_lock (ximagesink->x_lock);
1104   }
1105
1106   /* Handle Display events */
1107   while (XPending (ximagesink->xcontext->disp)) {
1108     XNextEvent (ximagesink->xcontext->disp, &e);
1109
1110     switch (e.type) {
1111       case ClientMessage:{
1112         Atom wm_delete;
1113
1114         wm_delete = XInternAtom (ximagesink->xcontext->disp,
1115             "WM_DELETE_WINDOW", False);
1116         if (wm_delete == (Atom) e.xclient.data.l[0]) {
1117           /* Handle window deletion by posting an error on the bus */
1118           GST_ELEMENT_ERROR (ximagesink, RESOURCE, NOT_FOUND,
1119               ("Output window was closed"), (NULL));
1120
1121           g_mutex_unlock (ximagesink->x_lock);
1122           gst_ximagesink_xwindow_destroy (ximagesink, ximagesink->xwindow);
1123           ximagesink->xwindow = NULL;
1124           g_mutex_lock (ximagesink->x_lock);
1125         }
1126         break;
1127       }
1128       default:
1129         break;
1130     }
1131   }
1132
1133   g_mutex_unlock (ximagesink->x_lock);
1134   g_mutex_unlock (ximagesink->flow_lock);
1135 }
1136
1137 static gpointer
1138 gst_ximagesink_event_thread (GstXImageSink * ximagesink)
1139 {
1140   g_return_val_if_fail (GST_IS_XIMAGESINK (ximagesink), NULL);
1141
1142   GST_OBJECT_LOCK (ximagesink);
1143   while (ximagesink->running) {
1144     GST_OBJECT_UNLOCK (ximagesink);
1145
1146     if (ximagesink->xwindow) {
1147       gst_ximagesink_handle_xevents (ximagesink);
1148     }
1149     /* FIXME: do we want to align this with the framerate or anything else? */
1150     g_usleep (G_USEC_PER_SEC / 20);
1151
1152     GST_OBJECT_LOCK (ximagesink);
1153   }
1154   GST_OBJECT_UNLOCK (ximagesink);
1155
1156   return NULL;
1157 }
1158
1159 static void
1160 gst_ximagesink_manage_event_thread (GstXImageSink * ximagesink)
1161 {
1162   GThread *thread = NULL;
1163
1164   /* don't start the thread too early */
1165   if (ximagesink->xcontext == NULL) {
1166     return;
1167   }
1168
1169   GST_OBJECT_LOCK (ximagesink);
1170   if (ximagesink->handle_expose || ximagesink->handle_events) {
1171     if (!ximagesink->event_thread) {
1172       /* Setup our event listening thread */
1173       GST_DEBUG_OBJECT (ximagesink, "run xevent thread, expose %d, events %d",
1174           ximagesink->handle_expose, ximagesink->handle_events);
1175       ximagesink->running = TRUE;
1176 #if !GLIB_CHECK_VERSION (2, 31, 0)
1177       ximagesink->event_thread = g_thread_create (
1178           (GThreadFunc) gst_ximagesink_event_thread, ximagesink, TRUE, NULL);
1179 #else
1180       ximagesink->event_thread = g_thread_try_new ("ximagesink-events",
1181           (GThreadFunc) gst_ximagesink_event_thread, ximagesink, NULL);
1182 #endif
1183     }
1184   } else {
1185     if (ximagesink->event_thread) {
1186       GST_DEBUG_OBJECT (ximagesink, "stop xevent thread, expose %d, events %d",
1187           ximagesink->handle_expose, ximagesink->handle_events);
1188       ximagesink->running = FALSE;
1189       /* grab thread and mark it as NULL */
1190       thread = ximagesink->event_thread;
1191       ximagesink->event_thread = NULL;
1192     }
1193   }
1194   GST_OBJECT_UNLOCK (ximagesink);
1195
1196   /* Wait for our event thread to finish */
1197   if (thread)
1198     g_thread_join (thread);
1199
1200 }
1201
1202
1203 /* This function calculates the pixel aspect ratio based on the properties
1204  * in the xcontext structure and stores it there. */
1205 static void
1206 gst_ximagesink_calculate_pixel_aspect_ratio (GstXContext * xcontext)
1207 {
1208   static const gint par[][2] = {
1209     {1, 1},                     /* regular screen */
1210     {16, 15},                   /* PAL TV */
1211     {11, 10},                   /* 525 line Rec.601 video */
1212     {54, 59},                   /* 625 line Rec.601 video */
1213     {64, 45},                   /* 1280x1024 on 16:9 display */
1214     {5, 3},                     /* 1280x1024 on 4:3 display */
1215     {4, 3}                      /*  800x600 on 16:9 display */
1216   };
1217   gint i;
1218   gint index;
1219   gdouble ratio;
1220   gdouble delta;
1221
1222 #define DELTA(idx) (ABS (ratio - ((gdouble) par[idx][0] / par[idx][1])))
1223
1224   /* first calculate the "real" ratio based on the X values;
1225    * which is the "physical" w/h divided by the w/h in pixels of the display */
1226   ratio = (gdouble) (xcontext->widthmm * xcontext->height)
1227       / (xcontext->heightmm * xcontext->width);
1228
1229   /* DirectFB's X in 720x576 reports the physical dimensions wrong, so
1230    * override here */
1231   if (xcontext->width == 720 && xcontext->height == 576) {
1232     ratio = 4.0 * 576 / (3.0 * 720);
1233   }
1234   GST_DEBUG ("calculated pixel aspect ratio: %f", ratio);
1235
1236   /* now find the one from par[][2] with the lowest delta to the real one */
1237   delta = DELTA (0);
1238   index = 0;
1239
1240   for (i = 1; i < sizeof (par) / (sizeof (gint) * 2); ++i) {
1241     gdouble this_delta = DELTA (i);
1242
1243     if (this_delta < delta) {
1244       index = i;
1245       delta = this_delta;
1246     }
1247   }
1248
1249   GST_DEBUG ("Decided on index %d (%d/%d)", index,
1250       par[index][0], par[index][1]);
1251
1252   g_free (xcontext->par);
1253   xcontext->par = g_new0 (GValue, 1);
1254   g_value_init (xcontext->par, GST_TYPE_FRACTION);
1255   gst_value_set_fraction (xcontext->par, par[index][0], par[index][1]);
1256   GST_DEBUG ("set xcontext PAR to %d/%d",
1257       gst_value_get_fraction_numerator (xcontext->par),
1258       gst_value_get_fraction_denominator (xcontext->par));
1259 }
1260
1261 /* This function gets the X Display and global info about it. Everything is
1262    stored in our object and will be cleaned when the object is disposed. Note
1263    here that caps for supported format are generated without any window or
1264    image creation */
1265 static GstXContext *
1266 gst_ximagesink_xcontext_get (GstXImageSink * ximagesink)
1267 {
1268   GstXContext *xcontext = NULL;
1269   XPixmapFormatValues *px_formats = NULL;
1270   gint nb_formats = 0, i;
1271
1272   g_return_val_if_fail (GST_IS_XIMAGESINK (ximagesink), NULL);
1273
1274   xcontext = g_new0 (GstXContext, 1);
1275
1276   g_mutex_lock (ximagesink->x_lock);
1277
1278   xcontext->disp = XOpenDisplay (ximagesink->display_name);
1279
1280   if (!xcontext->disp) {
1281     g_mutex_unlock (ximagesink->x_lock);
1282     g_free (xcontext);
1283     GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
1284         ("Could not initialise X output"), ("Could not open display"));
1285     return NULL;
1286   }
1287
1288   xcontext->screen = DefaultScreenOfDisplay (xcontext->disp);
1289   xcontext->screen_num = DefaultScreen (xcontext->disp);
1290   xcontext->visual = DefaultVisual (xcontext->disp, xcontext->screen_num);
1291   xcontext->root = DefaultRootWindow (xcontext->disp);
1292   xcontext->white = XWhitePixel (xcontext->disp, xcontext->screen_num);
1293   xcontext->black = XBlackPixel (xcontext->disp, xcontext->screen_num);
1294   xcontext->depth = DefaultDepthOfScreen (xcontext->screen);
1295
1296   xcontext->width = DisplayWidth (xcontext->disp, xcontext->screen_num);
1297   xcontext->height = DisplayHeight (xcontext->disp, xcontext->screen_num);
1298   xcontext->widthmm = DisplayWidthMM (xcontext->disp, xcontext->screen_num);
1299   xcontext->heightmm = DisplayHeightMM (xcontext->disp, xcontext->screen_num);
1300
1301   GST_DEBUG_OBJECT (ximagesink, "X reports %dx%d pixels and %d mm x %d mm",
1302       xcontext->width, xcontext->height, xcontext->widthmm, xcontext->heightmm);
1303
1304   gst_ximagesink_calculate_pixel_aspect_ratio (xcontext);
1305
1306   /* We get supported pixmap formats at supported depth */
1307   px_formats = XListPixmapFormats (xcontext->disp, &nb_formats);
1308
1309   if (!px_formats) {
1310     XCloseDisplay (xcontext->disp);
1311     g_mutex_unlock (ximagesink->x_lock);
1312     g_free (xcontext->par);
1313     g_free (xcontext);
1314     GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
1315         ("Could not get supported pixmap formats"), (NULL));
1316     return NULL;
1317   }
1318
1319   /* We get bpp value corresponding to our running depth */
1320   for (i = 0; i < nb_formats; i++) {
1321     if (px_formats[i].depth == xcontext->depth)
1322       xcontext->bpp = px_formats[i].bits_per_pixel;
1323   }
1324
1325   XFree (px_formats);
1326
1327   xcontext->endianness =
1328       (ImageByteOrder (xcontext->disp) ==
1329       LSBFirst) ? G_LITTLE_ENDIAN : G_BIG_ENDIAN;
1330
1331   /* Search for XShm extension support */
1332 #ifdef HAVE_XSHM
1333   if (XShmQueryExtension (xcontext->disp) &&
1334       gst_ximagesink_check_xshm_calls (ximagesink, xcontext)) {
1335     xcontext->use_xshm = TRUE;
1336     GST_DEBUG ("ximagesink is using XShm extension");
1337   } else
1338 #endif
1339   {
1340     xcontext->use_xshm = FALSE;
1341     GST_DEBUG ("ximagesink is not using XShm extension");
1342   }
1343
1344   /* our caps system handles 24/32bpp RGB as big-endian. */
1345   if ((xcontext->bpp == 24 || xcontext->bpp == 32) &&
1346       xcontext->endianness == G_LITTLE_ENDIAN) {
1347     xcontext->endianness = G_BIG_ENDIAN;
1348     xcontext->visual->red_mask = GUINT32_TO_BE (xcontext->visual->red_mask);
1349     xcontext->visual->green_mask = GUINT32_TO_BE (xcontext->visual->green_mask);
1350     xcontext->visual->blue_mask = GUINT32_TO_BE (xcontext->visual->blue_mask);
1351     if (xcontext->bpp == 24) {
1352       xcontext->visual->red_mask >>= 8;
1353       xcontext->visual->green_mask >>= 8;
1354       xcontext->visual->blue_mask >>= 8;
1355     }
1356   }
1357
1358   /* update object's par with calculated one if not set yet */
1359   if (!ximagesink->par) {
1360     ximagesink->par = g_new0 (GValue, 1);
1361     gst_value_init_and_copy (ximagesink->par, xcontext->par);
1362     GST_DEBUG_OBJECT (ximagesink, "set calculated PAR on object's PAR");
1363   }
1364   xcontext->caps = gst_caps_new_simple ("video/x-raw-rgb",
1365       "bpp", G_TYPE_INT, xcontext->bpp,
1366       "depth", G_TYPE_INT, xcontext->depth,
1367       "endianness", G_TYPE_INT, xcontext->endianness,
1368       "red_mask", G_TYPE_INT, xcontext->visual->red_mask,
1369       "green_mask", G_TYPE_INT, xcontext->visual->green_mask,
1370       "blue_mask", G_TYPE_INT, xcontext->visual->blue_mask,
1371       "width", GST_TYPE_INT_RANGE, 1, G_MAXINT,
1372       "height", GST_TYPE_INT_RANGE, 1, G_MAXINT,
1373       "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1, NULL);
1374   if (ximagesink->par) {
1375     int nom, den;
1376
1377     nom = gst_value_get_fraction_numerator (ximagesink->par);
1378     den = gst_value_get_fraction_denominator (ximagesink->par);
1379     gst_caps_set_simple (xcontext->caps, "pixel-aspect-ratio",
1380         GST_TYPE_FRACTION, nom, den, NULL);
1381   }
1382
1383   g_mutex_unlock (ximagesink->x_lock);
1384
1385   return xcontext;
1386 }
1387
1388 /* This function cleans the X context. Closing the Display and unrefing the
1389    caps for supported formats. */
1390 static void
1391 gst_ximagesink_xcontext_clear (GstXImageSink * ximagesink)
1392 {
1393   GstXContext *xcontext;
1394
1395   g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
1396
1397   GST_OBJECT_LOCK (ximagesink);
1398   if (ximagesink->xcontext == NULL) {
1399     GST_OBJECT_UNLOCK (ximagesink);
1400     return;
1401   }
1402
1403   /* Take the xcontext reference and NULL it while we
1404    * clean it up, so that any buffer-alloced buffers 
1405    * arriving after this will be freed correctly */
1406   xcontext = ximagesink->xcontext;
1407   ximagesink->xcontext = NULL;
1408
1409   GST_OBJECT_UNLOCK (ximagesink);
1410
1411   gst_caps_unref (xcontext->caps);
1412   g_free (xcontext->par);
1413   g_free (ximagesink->par);
1414   ximagesink->par = NULL;
1415
1416   if (xcontext->last_caps)
1417     gst_caps_replace (&xcontext->last_caps, NULL);
1418
1419   g_mutex_lock (ximagesink->x_lock);
1420
1421   XCloseDisplay (xcontext->disp);
1422
1423   g_mutex_unlock (ximagesink->x_lock);
1424
1425   g_free (xcontext);
1426 }
1427
1428 static void
1429 gst_ximagesink_bufferpool_clear (GstXImageSink * ximagesink)
1430 {
1431
1432   g_mutex_lock (ximagesink->pool_lock);
1433
1434   while (ximagesink->buffer_pool) {
1435     GstXImageBuffer *ximage = ximagesink->buffer_pool->data;
1436
1437     ximagesink->buffer_pool = g_slist_delete_link (ximagesink->buffer_pool,
1438         ximagesink->buffer_pool);
1439     gst_ximage_buffer_free (ximage);
1440   }
1441
1442   g_mutex_unlock (ximagesink->pool_lock);
1443 }
1444
1445 /* Element stuff */
1446
1447 static GstCaps *
1448 gst_ximagesink_getcaps (GstBaseSink * bsink)
1449 {
1450   GstXImageSink *ximagesink;
1451   GstCaps *caps;
1452   int i;
1453
1454   ximagesink = GST_XIMAGESINK (bsink);
1455
1456   if (ximagesink->xcontext)
1457     return gst_caps_ref (ximagesink->xcontext->caps);
1458
1459   /* get a template copy and add the pixel aspect ratio */
1460   caps =
1461       gst_caps_copy (gst_pad_get_pad_template_caps (GST_BASE_SINK
1462           (ximagesink)->sinkpad));
1463   for (i = 0; i < gst_caps_get_size (caps); ++i) {
1464     GstStructure *structure = gst_caps_get_structure (caps, i);
1465
1466     if (ximagesink->par) {
1467       int nom, den;
1468
1469       nom = gst_value_get_fraction_numerator (ximagesink->par);
1470       den = gst_value_get_fraction_denominator (ximagesink->par);
1471       gst_structure_set (structure, "pixel-aspect-ratio",
1472           GST_TYPE_FRACTION, nom, den, NULL);
1473     }
1474   }
1475
1476   return caps;
1477 }
1478
1479 static gboolean
1480 gst_ximagesink_setcaps (GstBaseSink * bsink, GstCaps * caps)
1481 {
1482   GstXImageSink *ximagesink;
1483   gboolean ret = TRUE;
1484   GstStructure *structure;
1485   const GValue *par;
1486   gint new_width, new_height;
1487   const GValue *fps;
1488
1489   ximagesink = GST_XIMAGESINK (bsink);
1490
1491   if (!ximagesink->xcontext)
1492     return FALSE;
1493
1494   GST_DEBUG_OBJECT (ximagesink,
1495       "sinkconnect possible caps %" GST_PTR_FORMAT " with given caps %"
1496       GST_PTR_FORMAT, ximagesink->xcontext->caps, caps);
1497
1498   /* We intersect those caps with our template to make sure they are correct */
1499   if (!gst_caps_can_intersect (ximagesink->xcontext->caps, caps))
1500     goto incompatible_caps;
1501
1502   structure = gst_caps_get_structure (caps, 0);
1503
1504   ret &= gst_structure_get_int (structure, "width", &new_width);
1505   ret &= gst_structure_get_int (structure, "height", &new_height);
1506   fps = gst_structure_get_value (structure, "framerate");
1507   ret &= (fps != NULL);
1508   if (!ret)
1509     return FALSE;
1510
1511   /* if the caps contain pixel-aspect-ratio, they have to match ours,
1512    * otherwise linking should fail */
1513   par = gst_structure_get_value (structure, "pixel-aspect-ratio");
1514   if (par) {
1515     if (ximagesink->par) {
1516       if (gst_value_compare (par, ximagesink->par) != GST_VALUE_EQUAL) {
1517         goto wrong_aspect;
1518       }
1519     } else if (ximagesink->xcontext->par) {
1520       if (gst_value_compare (par, ximagesink->xcontext->par) != GST_VALUE_EQUAL) {
1521         goto wrong_aspect;
1522       }
1523     }
1524   }
1525
1526   GST_VIDEO_SINK_WIDTH (ximagesink) = new_width;
1527   GST_VIDEO_SINK_HEIGHT (ximagesink) = new_height;
1528   ximagesink->fps_n = gst_value_get_fraction_numerator (fps);
1529   ximagesink->fps_d = gst_value_get_fraction_denominator (fps);
1530
1531   /* Notify application to set xwindow id now */
1532   g_mutex_lock (ximagesink->flow_lock);
1533   if (!ximagesink->xwindow) {
1534     g_mutex_unlock (ximagesink->flow_lock);
1535     gst_x_overlay_prepare_xwindow_id (GST_X_OVERLAY (ximagesink));
1536   } else {
1537     g_mutex_unlock (ximagesink->flow_lock);
1538   }
1539
1540   /* Creating our window and our image */
1541   if (GST_VIDEO_SINK_WIDTH (ximagesink) <= 0 ||
1542       GST_VIDEO_SINK_HEIGHT (ximagesink) <= 0) {
1543     GST_ELEMENT_ERROR (ximagesink, CORE, NEGOTIATION, (NULL),
1544         ("Invalid image size."));
1545     return FALSE;
1546   }
1547
1548   g_mutex_lock (ximagesink->flow_lock);
1549   if (!ximagesink->xwindow) {
1550     ximagesink->xwindow = gst_ximagesink_xwindow_new (ximagesink,
1551         GST_VIDEO_SINK_WIDTH (ximagesink), GST_VIDEO_SINK_HEIGHT (ximagesink));
1552   }
1553   /* Remember to draw borders for next frame */
1554   ximagesink->draw_border = TRUE;
1555   g_mutex_unlock (ximagesink->flow_lock);
1556
1557   /* If our ximage has changed we destroy it, next chain iteration will create
1558      a new one */
1559   if ((ximagesink->ximage) &&
1560       ((GST_VIDEO_SINK_WIDTH (ximagesink) != ximagesink->ximage->width) ||
1561           (GST_VIDEO_SINK_HEIGHT (ximagesink) != ximagesink->ximage->height))) {
1562     GST_DEBUG_OBJECT (ximagesink, "our image is not usable anymore, unref %p",
1563         ximagesink->ximage);
1564     gst_buffer_unref (GST_BUFFER_CAST (ximagesink->ximage));
1565     ximagesink->ximage = NULL;
1566   }
1567
1568   return TRUE;
1569
1570   /* ERRORS */
1571 incompatible_caps:
1572   {
1573     GST_ERROR_OBJECT (ximagesink, "caps incompatible");
1574     return FALSE;
1575   }
1576 wrong_aspect:
1577   {
1578     GST_INFO_OBJECT (ximagesink, "pixel aspect ratio does not match");
1579     return FALSE;
1580   }
1581 }
1582
1583 static GstStateChangeReturn
1584 gst_ximagesink_change_state (GstElement * element, GstStateChange transition)
1585 {
1586   GstXImageSink *ximagesink;
1587   GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
1588   GstXContext *xcontext = NULL;
1589
1590   ximagesink = GST_XIMAGESINK (element);
1591
1592   switch (transition) {
1593     case GST_STATE_CHANGE_NULL_TO_READY:
1594
1595       /* Initializing the XContext */
1596       if (ximagesink->xcontext == NULL) {
1597         xcontext = gst_ximagesink_xcontext_get (ximagesink);
1598         if (xcontext == NULL) {
1599           ret = GST_STATE_CHANGE_FAILURE;
1600           goto beach;
1601         }
1602         GST_OBJECT_LOCK (ximagesink);
1603         if (xcontext)
1604           ximagesink->xcontext = xcontext;
1605         GST_OBJECT_UNLOCK (ximagesink);
1606       }
1607
1608       /* call XSynchronize with the current value of synchronous */
1609       GST_DEBUG_OBJECT (ximagesink, "XSynchronize called with %s",
1610           ximagesink->synchronous ? "TRUE" : "FALSE");
1611       g_mutex_lock (ximagesink->x_lock);
1612       XSynchronize (ximagesink->xcontext->disp, ximagesink->synchronous);
1613       g_mutex_unlock (ximagesink->x_lock);
1614       gst_ximagesink_manage_event_thread (ximagesink);
1615       break;
1616     case GST_STATE_CHANGE_READY_TO_PAUSED:
1617       g_mutex_lock (ximagesink->flow_lock);
1618       if (ximagesink->xwindow)
1619         gst_ximagesink_xwindow_clear (ximagesink, ximagesink->xwindow);
1620       g_mutex_unlock (ximagesink->flow_lock);
1621       break;
1622     case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
1623       break;
1624     default:
1625       break;
1626   }
1627
1628   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
1629
1630   switch (transition) {
1631     case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
1632       break;
1633     case GST_STATE_CHANGE_PAUSED_TO_READY:
1634       ximagesink->fps_n = 0;
1635       ximagesink->fps_d = 1;
1636       GST_VIDEO_SINK_WIDTH (ximagesink) = 0;
1637       GST_VIDEO_SINK_HEIGHT (ximagesink) = 0;
1638       break;
1639     case GST_STATE_CHANGE_READY_TO_NULL:
1640       gst_ximagesink_reset (ximagesink);
1641       break;
1642     default:
1643       break;
1644   }
1645
1646 beach:
1647   return ret;
1648 }
1649
1650 static void
1651 gst_ximagesink_get_times (GstBaseSink * bsink, GstBuffer * buf,
1652     GstClockTime * start, GstClockTime * end)
1653 {
1654   GstXImageSink *ximagesink;
1655
1656   ximagesink = GST_XIMAGESINK (bsink);
1657
1658   if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
1659     *start = GST_BUFFER_TIMESTAMP (buf);
1660     if (GST_BUFFER_DURATION_IS_VALID (buf)) {
1661       *end = *start + GST_BUFFER_DURATION (buf);
1662     } else {
1663       if (ximagesink->fps_n > 0) {
1664         *end = *start +
1665             gst_util_uint64_scale_int (GST_SECOND, ximagesink->fps_d,
1666             ximagesink->fps_n);
1667       }
1668     }
1669   }
1670 }
1671
1672 static GstFlowReturn
1673 gst_ximagesink_show_frame (GstVideoSink * vsink, GstBuffer * buf)
1674 {
1675   GstXImageSink *ximagesink;
1676
1677   g_return_val_if_fail (buf != NULL, GST_FLOW_ERROR);
1678
1679   ximagesink = GST_XIMAGESINK (vsink);
1680
1681   /* This shouldn't really happen because state changes will fail
1682    * if the xcontext can't be allocated */
1683   if (!ximagesink->xcontext)
1684     return GST_FLOW_ERROR;
1685
1686   /* If this buffer has been allocated using our buffer management we simply
1687      put the ximage which is in the PRIVATE pointer */
1688   if (GST_IS_XIMAGE_BUFFER (buf)) {
1689     GST_LOG_OBJECT (ximagesink, "buffer from our pool, writing directly");
1690     if (!gst_ximagesink_ximage_put (ximagesink, GST_XIMAGE_BUFFER (buf)))
1691       goto no_window;
1692   } else {
1693     /* Else we have to copy the data into our private image, */
1694     /* if we have one... */
1695     GST_LOG_OBJECT (ximagesink, "normal buffer, copying from it");
1696     if (!ximagesink->ximage) {
1697       GST_DEBUG_OBJECT (ximagesink, "creating our ximage");
1698       ximagesink->ximage = gst_ximagesink_ximage_new (ximagesink,
1699           GST_BUFFER_CAPS (buf));
1700       if (!ximagesink->ximage)
1701         /* The create method should have posted an informative error */
1702         goto no_ximage;
1703
1704       if (ximagesink->ximage->size < GST_BUFFER_SIZE (buf)) {
1705         GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
1706             ("Failed to create output image buffer of %dx%d pixels",
1707                 ximagesink->ximage->width, ximagesink->ximage->height),
1708             ("XServer allocated buffer size did not match input buffer"));
1709
1710         gst_ximagesink_ximage_destroy (ximagesink, ximagesink->ximage);
1711         ximagesink->ximage = NULL;
1712         goto no_ximage;
1713       }
1714     }
1715     memcpy (GST_BUFFER_DATA (ximagesink->ximage), GST_BUFFER_DATA (buf),
1716         MIN (GST_BUFFER_SIZE (buf), ximagesink->ximage->size));
1717     if (!gst_ximagesink_ximage_put (ximagesink, ximagesink->ximage))
1718       goto no_window;
1719   }
1720
1721   return GST_FLOW_OK;
1722
1723   /* ERRORS */
1724 no_ximage:
1725   {
1726     /* No image available. That's very bad ! */
1727     GST_WARNING_OBJECT (ximagesink, "could not create image");
1728     return GST_FLOW_ERROR;
1729   }
1730 no_window:
1731   {
1732     /* No Window available to put our image into */
1733     GST_WARNING_OBJECT (ximagesink, "could not output image - no window");
1734     return GST_FLOW_ERROR;
1735   }
1736 }
1737
1738
1739 static gboolean
1740 gst_ximagesink_event (GstBaseSink * sink, GstEvent * event)
1741 {
1742   GstXImageSink *ximagesink = GST_XIMAGESINK (sink);
1743
1744   switch (GST_EVENT_TYPE (event)) {
1745     case GST_EVENT_TAG:{
1746       GstTagList *l;
1747       gchar *title = NULL;
1748
1749       gst_event_parse_tag (event, &l);
1750       gst_tag_list_get_string (l, GST_TAG_TITLE, &title);
1751
1752       if (title) {
1753         GST_DEBUG_OBJECT (ximagesink, "got tags, title='%s'", title);
1754         gst_ximagesink_xwindow_set_title (ximagesink, ximagesink->xwindow,
1755             title);
1756
1757         g_free (title);
1758       }
1759       break;
1760     }
1761     default:
1762       break;
1763   }
1764   if (GST_BASE_SINK_CLASS (parent_class)->event)
1765     return GST_BASE_SINK_CLASS (parent_class)->event (sink, event);
1766   else
1767     return TRUE;
1768 }
1769
1770
1771 /* Buffer management
1772  *
1773  * The buffer_alloc function must either return a buffer with given size and
1774  * caps or create a buffer with different caps attached to the buffer. This
1775  * last option is called reverse negotiation, ie, where the sink suggests a
1776  * different format from the upstream peer. 
1777  *
1778  * We try to do reverse negotiation when our geometry changes and we like a
1779  * resized buffer.
1780  */
1781 static GstFlowReturn
1782 gst_ximagesink_buffer_alloc (GstBaseSink * bsink, guint64 offset, guint size,
1783     GstCaps * caps, GstBuffer ** buf)
1784 {
1785   GstXImageSink *ximagesink;
1786   GstXImageBuffer *ximage = NULL;
1787   GstStructure *structure = NULL;
1788   GstFlowReturn ret = GST_FLOW_OK;
1789   GstCaps *alloc_caps;
1790   gboolean alloc_unref = FALSE;
1791   gint width, height;
1792   GstVideoRectangle dst, src, result;
1793   gboolean caps_accepted = FALSE;
1794
1795   ximagesink = GST_XIMAGESINK (bsink);
1796
1797   if (G_UNLIKELY (!caps)) {
1798     GST_WARNING_OBJECT (ximagesink, "have no caps, doing fallback allocation");
1799     *buf = NULL;
1800     ret = GST_FLOW_OK;
1801     goto beach;
1802   }
1803
1804   /* This shouldn't really happen because state changes will fail
1805    * if the xcontext can't be allocated */
1806   if (!ximagesink->xcontext)
1807     return GST_FLOW_ERROR;
1808
1809   GST_LOG_OBJECT (ximagesink,
1810       "a buffer of %d bytes was requested with caps %" GST_PTR_FORMAT
1811       " and offset %" G_GUINT64_FORMAT, size, caps, offset);
1812
1813   /* assume we're going to alloc what was requested, keep track of
1814    * whether we need to unref or not. When we suggest a new format 
1815    * upstream we will create a new caps that we need to unref. */
1816   alloc_caps = caps;
1817   alloc_unref = FALSE;
1818
1819   /* get struct to see what is requested */
1820   structure = gst_caps_get_structure (caps, 0);
1821   if (!gst_structure_get_int (structure, "width", &width) ||
1822       !gst_structure_get_int (structure, "height", &height)) {
1823     GST_WARNING_OBJECT (ximagesink, "invalid caps for buffer allocation %"
1824         GST_PTR_FORMAT, caps);
1825     ret = GST_FLOW_NOT_NEGOTIATED;
1826     goto beach;
1827   }
1828
1829   src.w = width;
1830   src.h = height;
1831
1832   /* We take the flow_lock because the window might go away */
1833   g_mutex_lock (ximagesink->flow_lock);
1834   if (!ximagesink->xwindow) {
1835     g_mutex_unlock (ximagesink->flow_lock);
1836     goto alloc;
1837   }
1838
1839   /* What is our geometry */
1840   dst.w = ximagesink->xwindow->width;
1841   dst.h = ximagesink->xwindow->height;
1842
1843   g_mutex_unlock (ximagesink->flow_lock);
1844
1845   if (ximagesink->keep_aspect) {
1846     GST_LOG_OBJECT (ximagesink, "enforcing aspect ratio in reverse caps "
1847         "negotiation");
1848     gst_video_sink_center_rect (src, dst, &result, TRUE);
1849   } else {
1850     GST_LOG_OBJECT (ximagesink, "trying to resize to window geometry "
1851         "ignoring aspect ratio");
1852     result.x = result.y = 0;
1853     result.w = dst.w;
1854     result.h = dst.h;
1855   }
1856
1857   /* We would like another geometry */
1858   if (width != result.w || height != result.h) {
1859     int nom, den;
1860     GstCaps *desired_caps;
1861     GstStructure *desired_struct;
1862
1863     /* make a copy of the incomming caps to create the new
1864      * suggestion. We can't use make_writable because we might
1865      * then destroy the original caps which we still need when the
1866      * peer does not accept the suggestion. */
1867     desired_caps = gst_caps_copy (caps);
1868     desired_struct = gst_caps_get_structure (desired_caps, 0);
1869
1870     GST_DEBUG ("we would love to receive a %dx%d video", result.w, result.h);
1871     gst_structure_set (desired_struct, "width", G_TYPE_INT, result.w, NULL);
1872     gst_structure_set (desired_struct, "height", G_TYPE_INT, result.h, NULL);
1873
1874     /* PAR property overrides the X calculated one */
1875     if (ximagesink->par) {
1876       nom = gst_value_get_fraction_numerator (ximagesink->par);
1877       den = gst_value_get_fraction_denominator (ximagesink->par);
1878       gst_structure_set (desired_struct, "pixel-aspect-ratio",
1879           GST_TYPE_FRACTION, nom, den, NULL);
1880     } else if (ximagesink->xcontext->par) {
1881       nom = gst_value_get_fraction_numerator (ximagesink->xcontext->par);
1882       den = gst_value_get_fraction_denominator (ximagesink->xcontext->par);
1883       gst_structure_set (desired_struct, "pixel-aspect-ratio",
1884           GST_TYPE_FRACTION, nom, den, NULL);
1885     }
1886
1887
1888     /* see if peer accepts our new suggestion, if there is no peer, this 
1889      * function returns true. */
1890     if (!ximagesink->xcontext->last_caps ||
1891         !gst_caps_is_equal (desired_caps, ximagesink->xcontext->last_caps)) {
1892       caps_accepted =
1893           gst_pad_peer_accept_caps (GST_VIDEO_SINK_PAD (ximagesink),
1894           desired_caps);
1895
1896       /* Suggestion failed, prevent future attempts for the same caps
1897        * to fail as well. */
1898       if (!caps_accepted)
1899         gst_caps_replace (&ximagesink->xcontext->last_caps, desired_caps);
1900     }
1901
1902     if (caps_accepted) {
1903       /* we will not alloc a buffer of the new suggested caps. Make sure
1904        * we also unref this new caps after we set it on the buffer. */
1905       alloc_caps = desired_caps;
1906       alloc_unref = TRUE;
1907       width = result.w;
1908       height = result.h;
1909       GST_DEBUG ("peer pad accepts our desired caps %" GST_PTR_FORMAT,
1910           desired_caps);
1911     } else {
1912       GST_DEBUG ("peer pad does not accept our desired caps %" GST_PTR_FORMAT,
1913           desired_caps);
1914       /* we alloc a buffer with the original incomming caps already in the
1915        * width and height variables */
1916       gst_caps_unref (desired_caps);
1917     }
1918   }
1919
1920 alloc:
1921   /* Inspect our buffer pool */
1922   g_mutex_lock (ximagesink->pool_lock);
1923   while (ximagesink->buffer_pool) {
1924     ximage = (GstXImageBuffer *) ximagesink->buffer_pool->data;
1925
1926     if (ximage) {
1927       /* Removing from the pool */
1928       ximagesink->buffer_pool = g_slist_delete_link (ximagesink->buffer_pool,
1929           ximagesink->buffer_pool);
1930
1931       /* If the ximage is invalid for our need, destroy */
1932       if ((ximage->width != width) || (ximage->height != height)) {
1933         gst_ximage_buffer_free (ximage);
1934         ximage = NULL;
1935       } else {
1936         /* We found a suitable ximage */
1937         break;
1938       }
1939     }
1940   }
1941   g_mutex_unlock (ximagesink->pool_lock);
1942
1943   /* We haven't found anything, creating a new one */
1944   if (!ximage) {
1945     ximage = gst_ximagesink_ximage_new (ximagesink, alloc_caps);
1946   }
1947   /* Now we should have a ximage, set appropriate caps on it */
1948   if (ximage) {
1949     /* Make sure the buffer is cleared of any previously used flags */
1950     GST_MINI_OBJECT_CAST (ximage)->flags = 0;
1951     gst_buffer_set_caps (GST_BUFFER_CAST (ximage), alloc_caps);
1952   }
1953
1954   /* could be our new reffed suggestion or the original unreffed caps */
1955   if (alloc_unref)
1956     gst_caps_unref (alloc_caps);
1957
1958   *buf = GST_BUFFER_CAST (ximage);
1959
1960 beach:
1961   return ret;
1962 }
1963
1964 /* Interfaces stuff */
1965
1966 static gboolean
1967 gst_ximagesink_interface_supported (GstImplementsInterface * iface, GType type)
1968 {
1969   if (type == GST_TYPE_NAVIGATION || type == GST_TYPE_X_OVERLAY)
1970     return TRUE;
1971   else
1972     return FALSE;
1973 }
1974
1975 static void
1976 gst_ximagesink_interface_init (GstImplementsInterfaceClass * klass)
1977 {
1978   klass->supported = gst_ximagesink_interface_supported;
1979 }
1980
1981 static void
1982 gst_ximagesink_navigation_send_event (GstNavigation * navigation,
1983     GstStructure * structure)
1984 {
1985   GstXImageSink *ximagesink = GST_XIMAGESINK (navigation);
1986   GstEvent *event;
1987   gint x_offset, y_offset;
1988   gdouble x, y;
1989   GstPad *pad = NULL;
1990
1991   event = gst_event_new_navigation (structure);
1992
1993   /* We are not converting the pointer coordinates as there's no hardware
1994      scaling done here. The only possible scaling is done by videoscale and
1995      videoscale will have to catch those events and tranform the coordinates
1996      to match the applied scaling. So here we just add the offset if the image
1997      is centered in the window.  */
1998
1999   /* We take the flow_lock while we look at the window */
2000   g_mutex_lock (ximagesink->flow_lock);
2001
2002   if (!ximagesink->xwindow) {
2003     g_mutex_unlock (ximagesink->flow_lock);
2004     return;
2005   }
2006
2007   x_offset = ximagesink->xwindow->width - GST_VIDEO_SINK_WIDTH (ximagesink);
2008   y_offset = ximagesink->xwindow->height - GST_VIDEO_SINK_HEIGHT (ximagesink);
2009
2010   g_mutex_unlock (ximagesink->flow_lock);
2011
2012   if (x_offset > 0 && gst_structure_get_double (structure, "pointer_x", &x)) {
2013     x -= x_offset / 2;
2014     gst_structure_set (structure, "pointer_x", G_TYPE_DOUBLE, x, NULL);
2015   }
2016   if (y_offset > 0 && gst_structure_get_double (structure, "pointer_y", &y)) {
2017     y -= y_offset / 2;
2018     gst_structure_set (structure, "pointer_y", G_TYPE_DOUBLE, y, NULL);
2019   }
2020
2021   pad = gst_pad_get_peer (GST_VIDEO_SINK_PAD (ximagesink));
2022
2023   if (GST_IS_PAD (pad) && GST_IS_EVENT (event)) {
2024     gst_pad_send_event (pad, event);
2025
2026     gst_object_unref (pad);
2027   }
2028 }
2029
2030 static void
2031 gst_ximagesink_navigation_init (GstNavigationInterface * iface)
2032 {
2033   iface->send_event = gst_ximagesink_navigation_send_event;
2034 }
2035
2036 static void
2037 gst_ximagesink_set_window_handle (GstXOverlay * overlay, guintptr id)
2038 {
2039   XID xwindow_id = id;
2040   GstXImageSink *ximagesink = GST_XIMAGESINK (overlay);
2041   GstXWindow *xwindow = NULL;
2042   XWindowAttributes attr;
2043
2044   /* We acquire the stream lock while setting this window in the element.
2045      We are basically cleaning tons of stuff replacing the old window, putting
2046      images while we do that would surely crash */
2047   g_mutex_lock (ximagesink->flow_lock);
2048
2049   /* If we already use that window return */
2050   if (ximagesink->xwindow && (xwindow_id == ximagesink->xwindow->win)) {
2051     g_mutex_unlock (ximagesink->flow_lock);
2052     return;
2053   }
2054
2055   /* If the element has not initialized the X11 context try to do so */
2056   if (!ximagesink->xcontext &&
2057       !(ximagesink->xcontext = gst_ximagesink_xcontext_get (ximagesink))) {
2058     g_mutex_unlock (ximagesink->flow_lock);
2059     /* we have thrown a GST_ELEMENT_ERROR now */
2060     return;
2061   }
2062
2063   /* If a window is there already we destroy it */
2064   if (ximagesink->xwindow) {
2065     gst_ximagesink_xwindow_destroy (ximagesink, ximagesink->xwindow);
2066     ximagesink->xwindow = NULL;
2067   }
2068
2069   /* If the xid is 0 we go back to an internal window */
2070   if (xwindow_id == 0) {
2071     /* If no width/height caps nego did not happen window will be created
2072        during caps nego then */
2073     if (GST_VIDEO_SINK_WIDTH (ximagesink) && GST_VIDEO_SINK_HEIGHT (ximagesink)) {
2074       xwindow = gst_ximagesink_xwindow_new (ximagesink,
2075           GST_VIDEO_SINK_WIDTH (ximagesink),
2076           GST_VIDEO_SINK_HEIGHT (ximagesink));
2077     }
2078   } else {
2079     xwindow = g_new0 (GstXWindow, 1);
2080
2081     xwindow->win = xwindow_id;
2082
2083     /* We get window geometry, set the event we want to receive,
2084        and create a GC */
2085     g_mutex_lock (ximagesink->x_lock);
2086     XGetWindowAttributes (ximagesink->xcontext->disp, xwindow->win, &attr);
2087     xwindow->width = attr.width;
2088     xwindow->height = attr.height;
2089     xwindow->internal = FALSE;
2090     if (ximagesink->handle_events) {
2091       XSelectInput (ximagesink->xcontext->disp, xwindow->win, ExposureMask |
2092           StructureNotifyMask | PointerMotionMask | KeyPressMask |
2093           KeyReleaseMask);
2094     }
2095
2096     xwindow->gc = XCreateGC (ximagesink->xcontext->disp, xwindow->win, 0, NULL);
2097     g_mutex_unlock (ximagesink->x_lock);
2098   }
2099
2100   if (xwindow)
2101     ximagesink->xwindow = xwindow;
2102
2103   g_mutex_unlock (ximagesink->flow_lock);
2104 }
2105
2106 static void
2107 gst_ximagesink_expose (GstXOverlay * overlay)
2108 {
2109   GstXImageSink *ximagesink = GST_XIMAGESINK (overlay);
2110
2111   gst_ximagesink_xwindow_update_geometry (ximagesink);
2112   gst_ximagesink_ximage_put (ximagesink, NULL);
2113 }
2114
2115 static void
2116 gst_ximagesink_set_event_handling (GstXOverlay * overlay,
2117     gboolean handle_events)
2118 {
2119   GstXImageSink *ximagesink = GST_XIMAGESINK (overlay);
2120
2121   ximagesink->handle_events = handle_events;
2122
2123   g_mutex_lock (ximagesink->flow_lock);
2124
2125   if (G_UNLIKELY (!ximagesink->xwindow)) {
2126     g_mutex_unlock (ximagesink->flow_lock);
2127     return;
2128   }
2129
2130   g_mutex_lock (ximagesink->x_lock);
2131
2132   if (handle_events) {
2133     if (ximagesink->xwindow->internal) {
2134       XSelectInput (ximagesink->xcontext->disp, ximagesink->xwindow->win,
2135           ExposureMask | StructureNotifyMask | PointerMotionMask |
2136           KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask);
2137     } else {
2138       XSelectInput (ximagesink->xcontext->disp, ximagesink->xwindow->win,
2139           ExposureMask | StructureNotifyMask | PointerMotionMask |
2140           KeyPressMask | KeyReleaseMask);
2141     }
2142   } else {
2143     XSelectInput (ximagesink->xcontext->disp, ximagesink->xwindow->win, 0);
2144   }
2145
2146   g_mutex_unlock (ximagesink->x_lock);
2147
2148   g_mutex_unlock (ximagesink->flow_lock);
2149 }
2150
2151 static void
2152 gst_ximagesink_xoverlay_init (GstXOverlayClass * iface)
2153 {
2154   iface->set_window_handle = gst_ximagesink_set_window_handle;
2155   iface->expose = gst_ximagesink_expose;
2156   iface->handle_events = gst_ximagesink_set_event_handling;
2157 }
2158
2159 /* =========================================== */
2160 /*                                             */
2161 /*              Init & Class init              */
2162 /*                                             */
2163 /* =========================================== */
2164
2165 static void
2166 gst_ximagesink_set_property (GObject * object, guint prop_id,
2167     const GValue * value, GParamSpec * pspec)
2168 {
2169   GstXImageSink *ximagesink;
2170
2171   g_return_if_fail (GST_IS_XIMAGESINK (object));
2172
2173   ximagesink = GST_XIMAGESINK (object);
2174
2175   switch (prop_id) {
2176     case PROP_DISPLAY:
2177       ximagesink->display_name = g_strdup (g_value_get_string (value));
2178       break;
2179     case PROP_SYNCHRONOUS:
2180       ximagesink->synchronous = g_value_get_boolean (value);
2181       if (ximagesink->xcontext) {
2182         GST_DEBUG_OBJECT (ximagesink, "XSynchronize called with %s",
2183             ximagesink->synchronous ? "TRUE" : "FALSE");
2184         g_mutex_lock (ximagesink->x_lock);
2185         XSynchronize (ximagesink->xcontext->disp, ximagesink->synchronous);
2186         g_mutex_unlock (ximagesink->x_lock);
2187       }
2188       break;
2189     case PROP_FORCE_ASPECT_RATIO:
2190       ximagesink->keep_aspect = g_value_get_boolean (value);
2191       break;
2192     case PROP_PIXEL_ASPECT_RATIO:
2193     {
2194       GValue *tmp;
2195
2196       tmp = g_new0 (GValue, 1);
2197       g_value_init (tmp, GST_TYPE_FRACTION);
2198
2199       if (!g_value_transform (value, tmp)) {
2200         GST_WARNING_OBJECT (ximagesink,
2201             "Could not transform string to aspect ratio");
2202         g_free (tmp);
2203       } else {
2204         GST_DEBUG_OBJECT (ximagesink, "set PAR to %d/%d",
2205             gst_value_get_fraction_numerator (tmp),
2206             gst_value_get_fraction_denominator (tmp));
2207         g_free (ximagesink->par);
2208         ximagesink->par = tmp;
2209       }
2210     }
2211       break;
2212     case PROP_HANDLE_EVENTS:
2213       gst_ximagesink_set_event_handling (GST_X_OVERLAY (ximagesink),
2214           g_value_get_boolean (value));
2215       gst_ximagesink_manage_event_thread (ximagesink);
2216       break;
2217     case PROP_HANDLE_EXPOSE:
2218       ximagesink->handle_expose = g_value_get_boolean (value);
2219       gst_ximagesink_manage_event_thread (ximagesink);
2220       break;
2221     default:
2222       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2223       break;
2224   }
2225 }
2226
2227 static void
2228 gst_ximagesink_get_property (GObject * object, guint prop_id,
2229     GValue * value, GParamSpec * pspec)
2230 {
2231   GstXImageSink *ximagesink;
2232
2233   g_return_if_fail (GST_IS_XIMAGESINK (object));
2234
2235   ximagesink = GST_XIMAGESINK (object);
2236
2237   switch (prop_id) {
2238     case PROP_DISPLAY:
2239       g_value_set_string (value, ximagesink->display_name);
2240       break;
2241     case PROP_SYNCHRONOUS:
2242       g_value_set_boolean (value, ximagesink->synchronous);
2243       break;
2244     case PROP_FORCE_ASPECT_RATIO:
2245       g_value_set_boolean (value, ximagesink->keep_aspect);
2246       break;
2247     case PROP_PIXEL_ASPECT_RATIO:
2248       if (ximagesink->par)
2249         g_value_transform (ximagesink->par, value);
2250       break;
2251     case PROP_HANDLE_EVENTS:
2252       g_value_set_boolean (value, ximagesink->handle_events);
2253       break;
2254     case PROP_HANDLE_EXPOSE:
2255       g_value_set_boolean (value, ximagesink->handle_expose);
2256       break;
2257     case PROP_WINDOW_WIDTH:
2258       if (ximagesink->xwindow)
2259         g_value_set_uint64 (value, ximagesink->xwindow->width);
2260       else
2261         g_value_set_uint64 (value, 0);
2262       break;
2263     case PROP_WINDOW_HEIGHT:
2264       if (ximagesink->xwindow)
2265         g_value_set_uint64 (value, ximagesink->xwindow->height);
2266       else
2267         g_value_set_uint64 (value, 0);
2268       break;
2269     default:
2270       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2271       break;
2272   }
2273 }
2274
2275 static void
2276 gst_ximagesink_reset (GstXImageSink * ximagesink)
2277 {
2278   GThread *thread;
2279
2280   GST_OBJECT_LOCK (ximagesink);
2281   ximagesink->running = FALSE;
2282   /* grab thread and mark it as NULL */
2283   thread = ximagesink->event_thread;
2284   ximagesink->event_thread = NULL;
2285   GST_OBJECT_UNLOCK (ximagesink);
2286
2287   /* Wait for our event thread to finish before we clean up our stuff. */
2288   if (thread)
2289     g_thread_join (thread);
2290
2291   if (ximagesink->ximage) {
2292     gst_buffer_unref (GST_BUFFER_CAST (ximagesink->ximage));
2293     ximagesink->ximage = NULL;
2294   }
2295   if (ximagesink->cur_image) {
2296     gst_buffer_unref (GST_BUFFER_CAST (ximagesink->cur_image));
2297     ximagesink->cur_image = NULL;
2298   }
2299
2300   gst_ximagesink_bufferpool_clear (ximagesink);
2301
2302   g_mutex_lock (ximagesink->flow_lock);
2303   if (ximagesink->xwindow) {
2304     gst_ximagesink_xwindow_clear (ximagesink, ximagesink->xwindow);
2305     gst_ximagesink_xwindow_destroy (ximagesink, ximagesink->xwindow);
2306     ximagesink->xwindow = NULL;
2307   }
2308   g_mutex_unlock (ximagesink->flow_lock);
2309
2310   gst_ximagesink_xcontext_clear (ximagesink);
2311 }
2312
2313 static void
2314 gst_ximagesink_finalize (GObject * object)
2315 {
2316   GstXImageSink *ximagesink;
2317
2318   ximagesink = GST_XIMAGESINK (object);
2319
2320   gst_ximagesink_reset (ximagesink);
2321
2322   if (ximagesink->display_name) {
2323     g_free (ximagesink->display_name);
2324     ximagesink->display_name = NULL;
2325   }
2326   if (ximagesink->par) {
2327     g_free (ximagesink->par);
2328     ximagesink->par = NULL;
2329   }
2330   if (ximagesink->x_lock) {
2331     g_mutex_free (ximagesink->x_lock);
2332     ximagesink->x_lock = NULL;
2333   }
2334   if (ximagesink->flow_lock) {
2335     g_mutex_free (ximagesink->flow_lock);
2336     ximagesink->flow_lock = NULL;
2337   }
2338   if (ximagesink->pool_lock) {
2339     g_mutex_free (ximagesink->pool_lock);
2340     ximagesink->pool_lock = NULL;
2341   }
2342
2343   g_free (ximagesink->media_title);
2344
2345   G_OBJECT_CLASS (parent_class)->finalize (object);
2346 }
2347
2348 static void
2349 gst_ximagesink_init (GstXImageSink * ximagesink)
2350 {
2351   ximagesink->display_name = NULL;
2352   ximagesink->xcontext = NULL;
2353   ximagesink->xwindow = NULL;
2354   ximagesink->ximage = NULL;
2355   ximagesink->cur_image = NULL;
2356
2357   ximagesink->event_thread = NULL;
2358   ximagesink->running = FALSE;
2359
2360   ximagesink->fps_n = 0;
2361   ximagesink->fps_d = 1;
2362
2363   ximagesink->x_lock = g_mutex_new ();
2364   ximagesink->flow_lock = g_mutex_new ();
2365
2366   ximagesink->par = NULL;
2367
2368   ximagesink->pool_lock = g_mutex_new ();
2369   ximagesink->buffer_pool = NULL;
2370
2371   ximagesink->synchronous = FALSE;
2372   ximagesink->keep_aspect = FALSE;
2373   ximagesink->handle_events = TRUE;
2374   ximagesink->handle_expose = TRUE;
2375 }
2376
2377 static void
2378 gst_ximagesink_base_init (gpointer g_class)
2379 {
2380   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
2381
2382   gst_element_class_set_details_simple (element_class,
2383       "Video sink", "Sink/Video",
2384       "A standard X based videosink", "Julien Moutte <julien@moutte.net>");
2385
2386   gst_element_class_add_static_pad_template (element_class,
2387       &gst_ximagesink_sink_template_factory);
2388 }
2389
2390 static void
2391 gst_ximagesink_class_init (GstXImageSinkClass * klass)
2392 {
2393   GObjectClass *gobject_class;
2394   GstElementClass *gstelement_class;
2395   GstBaseSinkClass *gstbasesink_class;
2396   GstVideoSinkClass *videosink_class;
2397
2398   gobject_class = (GObjectClass *) klass;
2399   gstelement_class = (GstElementClass *) klass;
2400   gstbasesink_class = (GstBaseSinkClass *) klass;
2401   videosink_class = (GstVideoSinkClass *) klass;
2402
2403   parent_class = g_type_class_peek_parent (klass);
2404
2405   gobject_class->finalize = gst_ximagesink_finalize;
2406   gobject_class->set_property = gst_ximagesink_set_property;
2407   gobject_class->get_property = gst_ximagesink_get_property;
2408
2409   g_object_class_install_property (gobject_class, PROP_DISPLAY,
2410       g_param_spec_string ("display", "Display", "X Display name",
2411           NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2412   g_object_class_install_property (gobject_class, PROP_SYNCHRONOUS,
2413       g_param_spec_boolean ("synchronous", "Synchronous",
2414           "When enabled, runs the X display in synchronous mode. "
2415           "(unrelated to A/V sync, used only for debugging)", FALSE,
2416           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2417   g_object_class_install_property (gobject_class, PROP_FORCE_ASPECT_RATIO,
2418       g_param_spec_boolean ("force-aspect-ratio", "Force aspect ratio",
2419           "When enabled, reverse caps negotiation (scaling) will respect "
2420           "original aspect ratio", FALSE,
2421           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2422   g_object_class_install_property (gobject_class, PROP_PIXEL_ASPECT_RATIO,
2423       g_param_spec_string ("pixel-aspect-ratio", "Pixel Aspect Ratio",
2424           "The pixel aspect ratio of the device", "1/1",
2425           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2426   g_object_class_install_property (gobject_class, PROP_HANDLE_EVENTS,
2427       g_param_spec_boolean ("handle-events", "Handle XEvents",
2428           "When enabled, XEvents will be selected and handled", TRUE,
2429           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2430   g_object_class_install_property (gobject_class, PROP_HANDLE_EXPOSE,
2431       g_param_spec_boolean ("handle-expose", "Handle expose",
2432           "When enabled, "
2433           "the current frame will always be drawn in response to X Expose "
2434           "events", TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2435
2436   /**
2437    * GstXImageSink:window-width
2438    *
2439    * Actual width of the video window.
2440    *
2441    * Since: 0.10.32
2442    */
2443   g_object_class_install_property (gobject_class, PROP_WINDOW_WIDTH,
2444       g_param_spec_uint64 ("window-width", "window-width",
2445           "Width of the window", 0, G_MAXUINT64, 0,
2446           G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
2447
2448   /**
2449    * GstXImageSink:window-height
2450    *
2451    * Actual height of the video window.
2452    *
2453    * Since: 0.10.32
2454    */
2455   g_object_class_install_property (gobject_class, PROP_WINDOW_HEIGHT,
2456       g_param_spec_uint64 ("window-height", "window-height",
2457           "Height of the window", 0, G_MAXUINT64, 0,
2458           G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
2459
2460   gstelement_class->change_state = gst_ximagesink_change_state;
2461
2462   gstbasesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_ximagesink_getcaps);
2463   gstbasesink_class->set_caps = GST_DEBUG_FUNCPTR (gst_ximagesink_setcaps);
2464   gstbasesink_class->buffer_alloc =
2465       GST_DEBUG_FUNCPTR (gst_ximagesink_buffer_alloc);
2466   gstbasesink_class->get_times = GST_DEBUG_FUNCPTR (gst_ximagesink_get_times);
2467   gstbasesink_class->event = GST_DEBUG_FUNCPTR (gst_ximagesink_event);
2468
2469   videosink_class->show_frame = GST_DEBUG_FUNCPTR (gst_ximagesink_show_frame);
2470 }
2471
2472 /* ============================================================= */
2473 /*                                                               */
2474 /*                       Public Methods                          */
2475 /*                                                               */
2476 /* ============================================================= */
2477
2478 /* =========================================== */
2479 /*                                             */
2480 /*          Object typing & Creation           */
2481 /*                                             */
2482 /* =========================================== */
2483
2484 GType
2485 gst_ximagesink_get_type (void)
2486 {
2487   static GType ximagesink_type = 0;
2488
2489   if (!ximagesink_type) {
2490     static const GTypeInfo ximagesink_info = {
2491       sizeof (GstXImageSinkClass),
2492       gst_ximagesink_base_init,
2493       NULL,
2494       (GClassInitFunc) gst_ximagesink_class_init,
2495       NULL,
2496       NULL,
2497       sizeof (GstXImageSink), 0, (GInstanceInitFunc) gst_ximagesink_init,
2498     };
2499     static const GInterfaceInfo iface_info = {
2500       (GInterfaceInitFunc) gst_ximagesink_interface_init, NULL, NULL,
2501     };
2502     static const GInterfaceInfo navigation_info = {
2503       (GInterfaceInitFunc) gst_ximagesink_navigation_init, NULL, NULL,
2504     };
2505     static const GInterfaceInfo overlay_info = {
2506       (GInterfaceInitFunc) gst_ximagesink_xoverlay_init, NULL, NULL,
2507     };
2508
2509     ximagesink_type = g_type_register_static (GST_TYPE_VIDEO_SINK,
2510         "GstXImageSink", &ximagesink_info, 0);
2511
2512     g_type_add_interface_static (ximagesink_type, GST_TYPE_IMPLEMENTS_INTERFACE,
2513         &iface_info);
2514     g_type_add_interface_static (ximagesink_type, GST_TYPE_NAVIGATION,
2515         &navigation_info);
2516     g_type_add_interface_static (ximagesink_type, GST_TYPE_X_OVERLAY,
2517         &overlay_info);
2518
2519     /* register type and create class in a more safe place instead of at
2520      * runtime since the type registration and class creation is not
2521      * threadsafe. */
2522     g_type_class_ref (gst_ximage_buffer_get_type ());
2523   }
2524
2525   return ximagesink_type;
2526 }