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