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