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