Merge branch 'master' into 0.11
[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  * XImageSink renders video frames to a drawable (XWindow) on a local or remote
24  * display. This element can receive a Window ID from the application through
25  * the XOverlay interface and will then render video frames in this drawable.
26  * If no Window ID was provided by the application, the element will create its
27  * own internal window and render into it.
28  *
29  * <refsect2>
30  * <title>Scaling</title>
31  * <para>
32  * As standard XImage rendering to a drawable is not scaled, XImageSink will use
33  * reverse caps negotiation to try to get scaled video frames for the drawable.
34  * This is accomplished by asking the peer pad if it accepts some different caps
35  * which in most cases implies that there is a scaling element in the pipeline,
36  * or that an element generating the video frames can generate them with a 
37  * different geometry. This mechanism is handled during buffer allocations, for
38  * each allocation request the video sink will check the drawable geometry, look
39  * at the #GstXImageSink:force-aspect-ratio property, calculate the geometry of
40  * desired video frames and then check that the peer pad accept those new caps.
41  * If it does it will then allocate a buffer in video memory with this new
42  * geometry and return it with the new caps.
43  * </para>
44  * </refsect2>
45  * <refsect2>
46  * <title>Events</title>
47  * <para>
48  * XImageSink creates a thread to handle events coming from the drawable. There
49  * are several kind of events that can be grouped in 2 big categories: input 
50  * events and window state related events. Input events will be translated to
51  * navigation events and pushed upstream for other elements to react on them.
52  * This includes events such as pointer moves, key press/release, clicks etc...
53  * Other events are used to handle the drawable appearance even when the data
54  * is not flowing (GST_STATE_PAUSED). That means that even when the element is
55  * paused, it will receive expose events from the drawable and draw the latest
56  * frame with correct borders/aspect-ratio.
57  * </para>
58  * </refsect2>
59  * <refsect2>
60  * <title>Pixel aspect ratio</title>
61  * <para>
62  * When changing state to GST_STATE_READY, XImageSink will open a connection to
63  * the display specified in the #GstXImageSink:display property or the default
64  * display if nothing specified. Once this connection is open it will inspect 
65  * the display configuration including the physical display geometry and 
66  * then calculate the pixel aspect ratio. When caps negotiation will occur, the
67  * video sink will set the calculated pixel aspect ratio on the caps to make 
68  * sure that incoming video frames will have the correct pixel aspect ratio for
69  * this display. Sometimes the calculated pixel aspect ratio can be wrong, it is
70  * then possible to enforce a specific pixel aspect ratio using the
71  * #GstXImageSink:pixel-aspect-ratio property.
72  * </para>
73  * </refsect2>
74  * <refsect2>
75  * <title>Examples</title>
76  * |[
77  * gst-launch -v videotestsrc ! queue ! ximagesink
78  * ]| A pipeline to test reverse negotiation. When the test video signal appears
79  * you can resize the window and see that scaled buffers of the desired size are
80  * going to arrive with a short delay. This illustrates how buffers of desired
81  * size are allocated along the way. If you take away the queue, scaling will
82  * happen almost immediately.
83  * |[
84  * gst-launch -v videotestsrc ! navigationtest ! ffmpegcolorspace ! ximagesink
85  * ]| A pipeline to test navigation events.
86  * While moving the mouse pointer over the test signal you will see a black box
87  * following the mouse pointer. If you press the mouse button somewhere on the 
88  * video and release it somewhere else a green box will appear where you pressed
89  * the button and a red one where you released it. (The navigationtest element
90  * is part of gst-plugins-good.)
91  * |[
92  * gst-launch -v videotestsrc ! video/x-raw-rgb, pixel-aspect-ratio=(fraction)4/3 ! videoscale ! ximagesink
93  * ]| This is faking a 4/3 pixel aspect ratio caps on video frames produced by
94  * videotestsrc, in most cases the pixel aspect ratio of the display will be
95  * 1/1. This means that videoscale will have to do the scaling to convert 
96  * incoming frames to a size that will match the display pixel aspect ratio
97  * (from 320x240 to 320x180 in this case). Note that you might have to escape 
98  * some characters for your shell like '\(fraction\)'.
99  * </refsect2>
100  */
101
102 #ifdef HAVE_CONFIG_H
103 #include "config.h"
104 #endif
105
106 /* Our interfaces */
107 #include <gst/interfaces/navigation.h>
108 #include <gst/interfaces/xoverlay.h>
109
110 #include <gst/video/gstmetavideo.h>
111
112 /* Object header */
113 #include "ximagesink.h"
114
115 /* Debugging category */
116 #include <gst/gstinfo.h>
117
118 GST_DEBUG_CATEGORY_EXTERN (gst_debug_ximagesink);
119 #define GST_CAT_DEFAULT gst_debug_ximagesink
120
121 typedef struct
122 {
123   unsigned long flags;
124   unsigned long functions;
125   unsigned long decorations;
126   long input_mode;
127   unsigned long status;
128 }
129 MotifWmHints, MwmHints;
130
131 #define MWM_HINTS_DECORATIONS   (1L << 1)
132
133 static void gst_ximagesink_reset (GstXImageSink * ximagesink);
134 static void gst_ximagesink_xwindow_update_geometry (GstXImageSink * ximagesink);
135 static void gst_ximagesink_expose (GstXOverlay * overlay);
136
137 static GstStaticPadTemplate gst_ximagesink_sink_template_factory =
138 GST_STATIC_PAD_TEMPLATE ("sink",
139     GST_PAD_SINK,
140     GST_PAD_ALWAYS,
141     GST_STATIC_CAPS ("video/x-raw-rgb, "
142         "framerate = (fraction) [ 0, MAX ], "
143         "width = (int) [ 1, MAX ], " "height = (int) [ 1, MAX ]")
144     );
145
146 enum
147 {
148   PROP_0,
149   PROP_DISPLAY,
150   PROP_SYNCHRONOUS,
151   PROP_PIXEL_ASPECT_RATIO,
152   PROP_FORCE_ASPECT_RATIO,
153   PROP_HANDLE_EVENTS,
154   PROP_HANDLE_EXPOSE,
155   PROP_WINDOW_WIDTH,
156   PROP_WINDOW_HEIGHT
157 };
158
159 /* ============================================================= */
160 /*                                                               */
161 /*                       Public Methods                          */
162 /*                                                               */
163 /* ============================================================= */
164
165 /* =========================================== */
166 /*                                             */
167 /*          Object typing & Creation           */
168 /*                                             */
169 /* =========================================== */
170 static void gst_ximagesink_interface_init (GstImplementsInterfaceClass * klass);
171 static void gst_ximagesink_navigation_init (GstNavigationInterface * klass);
172 static void gst_ximagesink_xoverlay_init (GstXOverlayClass * klass);
173 #define gst_ximagesink_parent_class parent_class
174 G_DEFINE_TYPE_WITH_CODE (GstXImageSink, gst_ximagesink, GST_TYPE_VIDEO_SINK,
175     G_IMPLEMENT_INTERFACE (GST_TYPE_IMPLEMENTS_INTERFACE,
176         gst_ximagesink_interface_init);
177     G_IMPLEMENT_INTERFACE (GST_TYPE_NAVIGATION, gst_ximagesink_navigation_init);
178     G_IMPLEMENT_INTERFACE (GST_TYPE_X_OVERLAY, gst_ximagesink_xoverlay_init));
179
180 /* ============================================================= */
181 /*                                                               */
182 /*                       Private Methods                         */
183 /*                                                               */
184 /* ============================================================= */
185
186 /* X11 stuff */
187
188 /* We are called with the x_lock taken */
189 static void
190 gst_ximagesink_xwindow_draw_borders (GstXImageSink * ximagesink,
191     GstXWindow * xwindow, GstVideoRectangle rect)
192 {
193   g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
194   g_return_if_fail (xwindow != NULL);
195
196   XSetForeground (ximagesink->xcontext->disp, xwindow->gc,
197       ximagesink->xcontext->black);
198
199   /* Left border */
200   if (rect.x > 0) {
201     XFillRectangle (ximagesink->xcontext->disp, xwindow->win, xwindow->gc,
202         0, 0, rect.x, xwindow->height);
203   }
204
205   /* Right border */
206   if ((rect.x + rect.w) < xwindow->width) {
207     XFillRectangle (ximagesink->xcontext->disp, xwindow->win, xwindow->gc,
208         rect.x + rect.w, 0, xwindow->width, xwindow->height);
209   }
210
211   /* Top border */
212   if (rect.y > 0) {
213     XFillRectangle (ximagesink->xcontext->disp, xwindow->win, xwindow->gc,
214         0, 0, xwindow->width, rect.y);
215   }
216
217   /* Bottom border */
218   if ((rect.y + rect.h) < xwindow->height) {
219     XFillRectangle (ximagesink->xcontext->disp, xwindow->win, xwindow->gc,
220         0, rect.y + rect.h, xwindow->width, xwindow->height);
221   }
222 }
223
224 /* This function puts a GstXImageBuffer on a GstXImageSink's window */
225 static gboolean
226 gst_ximagesink_ximage_put (GstXImageSink * ximagesink, GstBuffer * ximage)
227 {
228   GstMetaXImage *meta;
229   GstVideoRectangle src, dst, result;
230   gboolean draw_border = FALSE;
231
232   /* We take the flow_lock. If expose is in there we don't want to run
233      concurrently from the data flow thread */
234   g_mutex_lock (ximagesink->flow_lock);
235
236   if (G_UNLIKELY (ximagesink->xwindow == NULL)) {
237     g_mutex_unlock (ximagesink->flow_lock);
238     return FALSE;
239   }
240
241   /* Draw borders when displaying the first frame. After this
242      draw borders only on expose event or caps change (ximagesink->draw_border = TRUE). */
243   if (!ximagesink->cur_image || ximagesink->draw_border) {
244     draw_border = TRUE;
245   }
246
247   /* Store a reference to the last image we put, lose the previous one */
248   if (ximage && ximagesink->cur_image != ximage) {
249     if (ximagesink->cur_image) {
250       GST_LOG_OBJECT (ximagesink, "unreffing %p", ximagesink->cur_image);
251       gst_buffer_unref (ximagesink->cur_image);
252     }
253     GST_LOG_OBJECT (ximagesink, "reffing %p as our current image", ximage);
254     ximagesink->cur_image = gst_buffer_ref (ximage);
255   }
256
257   /* Expose sends a NULL image, we take the latest frame */
258   if (!ximage) {
259     draw_border = TRUE;
260     if (ximagesink->cur_image) {
261       ximage = ximagesink->cur_image;
262     } else {
263       g_mutex_unlock (ximagesink->flow_lock);
264       return TRUE;
265     }
266   }
267
268   meta = gst_buffer_get_meta_ximage (ximage);
269
270   src.w = meta->width;
271   src.h = meta->height;
272   dst.w = ximagesink->xwindow->width;
273   dst.h = ximagesink->xwindow->height;
274
275   gst_video_sink_center_rect (src, dst, &result, FALSE);
276
277   g_mutex_lock (ximagesink->x_lock);
278
279   if (draw_border) {
280     gst_ximagesink_xwindow_draw_borders (ximagesink, ximagesink->xwindow,
281         result);
282     ximagesink->draw_border = FALSE;
283   }
284 #ifdef HAVE_XSHM
285   if (ximagesink->xcontext->use_xshm) {
286     GST_LOG_OBJECT (ximagesink,
287         "XShmPutImage on %p, src: %d, %d - dest: %d, %d, dim: %dx%d, win %dx%d",
288         ximage, 0, 0, result.x, result.y, result.w, result.h,
289         ximagesink->xwindow->width, ximagesink->xwindow->height);
290     XShmPutImage (ximagesink->xcontext->disp, ximagesink->xwindow->win,
291         ximagesink->xwindow->gc, meta->ximage, 0, 0, result.x, result.y,
292         result.w, result.h, FALSE);
293   } else
294 #endif /* HAVE_XSHM */
295   {
296     GST_LOG_OBJECT (ximagesink,
297         "XPutImage on %p, src: %d, %d - dest: %d, %d, dim: %dx%d, win %dx%d",
298         ximage, 0, 0, result.x, result.y, result.w, result.h,
299         ximagesink->xwindow->width, ximagesink->xwindow->height);
300     XPutImage (ximagesink->xcontext->disp, ximagesink->xwindow->win,
301         ximagesink->xwindow->gc, meta->ximage, 0, 0, result.x, result.y,
302         result.w, result.h);
303   }
304
305   XSync (ximagesink->xcontext->disp, FALSE);
306
307   g_mutex_unlock (ximagesink->x_lock);
308
309   g_mutex_unlock (ximagesink->flow_lock);
310
311   return TRUE;
312 }
313
314 static gboolean
315 gst_ximagesink_xwindow_decorate (GstXImageSink * ximagesink,
316     GstXWindow * window)
317 {
318   Atom hints_atom = None;
319   MotifWmHints *hints;
320
321   g_return_val_if_fail (GST_IS_XIMAGESINK (ximagesink), FALSE);
322   g_return_val_if_fail (window != NULL, FALSE);
323
324   g_mutex_lock (ximagesink->x_lock);
325
326   hints_atom = XInternAtom (ximagesink->xcontext->disp, "_MOTIF_WM_HINTS",
327       True);
328   if (hints_atom == None) {
329     g_mutex_unlock (ximagesink->x_lock);
330     return FALSE;
331   }
332
333   hints = g_malloc0 (sizeof (MotifWmHints));
334
335   hints->flags |= MWM_HINTS_DECORATIONS;
336   hints->decorations = 1 << 0;
337
338   XChangeProperty (ximagesink->xcontext->disp, window->win,
339       hints_atom, hints_atom, 32, PropModeReplace,
340       (guchar *) hints, sizeof (MotifWmHints) / sizeof (long));
341
342   XSync (ximagesink->xcontext->disp, FALSE);
343
344   g_mutex_unlock (ximagesink->x_lock);
345
346   g_free (hints);
347
348   return TRUE;
349 }
350
351 static void
352 gst_ximagesink_xwindow_set_title (GstXImageSink * ximagesink,
353     GstXWindow * xwindow, const gchar * media_title)
354 {
355   if (media_title) {
356     g_free (ximagesink->media_title);
357     ximagesink->media_title = g_strdup (media_title);
358   }
359   if (xwindow) {
360     /* we have a window */
361     if (xwindow->internal) {
362       XTextProperty xproperty;
363       const gchar *app_name;
364       const gchar *title = NULL;
365       gchar *title_mem = NULL;
366
367       /* set application name as a title */
368       app_name = g_get_application_name ();
369
370       if (app_name && ximagesink->media_title) {
371         title = title_mem = g_strconcat (ximagesink->media_title, " : ",
372             app_name, NULL);
373       } else if (app_name) {
374         title = app_name;
375       } else if (ximagesink->media_title) {
376         title = ximagesink->media_title;
377       }
378
379       if (title) {
380         if ((XStringListToTextProperty (((char **) &title), 1,
381                     &xproperty)) != 0) {
382           XSetWMName (ximagesink->xcontext->disp, xwindow->win, &xproperty);
383           XFree (xproperty.value);
384         }
385
386         g_free (title_mem);
387       }
388     }
389   }
390 }
391
392 /* This function handles a GstXWindow creation */
393 static GstXWindow *
394 gst_ximagesink_xwindow_new (GstXImageSink * ximagesink, gint width, gint height)
395 {
396   GstXWindow *xwindow = NULL;
397   XGCValues values;
398
399   g_return_val_if_fail (GST_IS_XIMAGESINK (ximagesink), NULL);
400
401   xwindow = g_new0 (GstXWindow, 1);
402
403   xwindow->width = width;
404   xwindow->height = height;
405   xwindow->internal = TRUE;
406
407   g_mutex_lock (ximagesink->x_lock);
408
409   xwindow->win = XCreateSimpleWindow (ximagesink->xcontext->disp,
410       ximagesink->xcontext->root,
411       0, 0, width, height, 0, 0, ximagesink->xcontext->black);
412
413   /* We have to do that to prevent X from redrawing the background on 
414      ConfigureNotify. This takes away flickering of video when resizing. */
415   XSetWindowBackgroundPixmap (ximagesink->xcontext->disp, xwindow->win, None);
416
417   /* set application name as a title */
418   gst_ximagesink_xwindow_set_title (ximagesink, xwindow, NULL);
419
420   if (ximagesink->handle_events) {
421     Atom wm_delete;
422
423     XSelectInput (ximagesink->xcontext->disp, xwindow->win, ExposureMask |
424         StructureNotifyMask | PointerMotionMask | KeyPressMask |
425         KeyReleaseMask | ButtonPressMask | ButtonReleaseMask);
426
427     /* Tell the window manager we'd like delete client messages instead of
428      * being killed */
429     wm_delete = XInternAtom (ximagesink->xcontext->disp,
430         "WM_DELETE_WINDOW", False);
431     (void) XSetWMProtocols (ximagesink->xcontext->disp, xwindow->win,
432         &wm_delete, 1);
433   }
434
435   xwindow->gc = XCreateGC (ximagesink->xcontext->disp, xwindow->win,
436       0, &values);
437
438   XMapRaised (ximagesink->xcontext->disp, xwindow->win);
439
440   XSync (ximagesink->xcontext->disp, FALSE);
441
442   g_mutex_unlock (ximagesink->x_lock);
443
444   gst_ximagesink_xwindow_decorate (ximagesink, xwindow);
445
446   gst_x_overlay_got_window_handle (GST_X_OVERLAY (ximagesink), xwindow->win);
447
448   return xwindow;
449 }
450
451 /* This function destroys a GstXWindow */
452 static void
453 gst_ximagesink_xwindow_destroy (GstXImageSink * ximagesink,
454     GstXWindow * xwindow)
455 {
456   g_return_if_fail (xwindow != NULL);
457   g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
458
459   g_mutex_lock (ximagesink->x_lock);
460
461   /* If we did not create that window we just free the GC and let it live */
462   if (xwindow->internal)
463     XDestroyWindow (ximagesink->xcontext->disp, xwindow->win);
464   else
465     XSelectInput (ximagesink->xcontext->disp, xwindow->win, 0);
466
467   XFreeGC (ximagesink->xcontext->disp, xwindow->gc);
468
469   XSync (ximagesink->xcontext->disp, FALSE);
470
471   g_mutex_unlock (ximagesink->x_lock);
472
473   g_free (xwindow);
474 }
475
476 static void
477 gst_ximagesink_xwindow_update_geometry (GstXImageSink * ximagesink)
478 {
479   XWindowAttributes attr;
480   gboolean reconfigure;
481
482   g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
483
484   /* Update the window geometry */
485   g_mutex_lock (ximagesink->x_lock);
486   if (G_UNLIKELY (ximagesink->xwindow == NULL)) {
487     g_mutex_unlock (ximagesink->x_lock);
488     return;
489   }
490
491   XGetWindowAttributes (ximagesink->xcontext->disp,
492       ximagesink->xwindow->win, &attr);
493
494   /* Check if we would suggest a different width/height now */
495   reconfigure = (ximagesink->xwindow->width != attr.width)
496       || (ximagesink->xwindow->height != attr.height);
497   ximagesink->xwindow->width = attr.width;
498   ximagesink->xwindow->height = attr.height;
499
500   g_mutex_unlock (ximagesink->x_lock);
501
502   if (reconfigure)
503     gst_pad_push_event (GST_BASE_SINK (ximagesink)->sinkpad,
504         gst_event_new_reconfigure ());
505 }
506
507 static void
508 gst_ximagesink_xwindow_clear (GstXImageSink * ximagesink, GstXWindow * xwindow)
509 {
510   g_return_if_fail (xwindow != NULL);
511   g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
512
513   g_mutex_lock (ximagesink->x_lock);
514
515   XSetForeground (ximagesink->xcontext->disp, xwindow->gc,
516       ximagesink->xcontext->black);
517
518   XFillRectangle (ximagesink->xcontext->disp, xwindow->win, xwindow->gc,
519       0, 0, xwindow->width, xwindow->height);
520
521   XSync (ximagesink->xcontext->disp, FALSE);
522
523   g_mutex_unlock (ximagesink->x_lock);
524 }
525
526 /* This function handles XEvents that might be in the queue. It generates
527    GstEvent that will be sent upstream in the pipeline to handle interactivity
528    and navigation.*/
529 static void
530 gst_ximagesink_handle_xevents (GstXImageSink * ximagesink)
531 {
532   XEvent e;
533   guint pointer_x = 0, pointer_y = 0;
534   gboolean pointer_moved = FALSE;
535   gboolean exposed = FALSE, configured = FALSE;
536
537   g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
538
539   /* Then we get all pointer motion events, only the last position is
540      interesting. */
541   g_mutex_lock (ximagesink->flow_lock);
542   g_mutex_lock (ximagesink->x_lock);
543   while (XCheckWindowEvent (ximagesink->xcontext->disp,
544           ximagesink->xwindow->win, PointerMotionMask, &e)) {
545     g_mutex_unlock (ximagesink->x_lock);
546     g_mutex_unlock (ximagesink->flow_lock);
547
548     switch (e.type) {
549       case MotionNotify:
550         pointer_x = e.xmotion.x;
551         pointer_y = e.xmotion.y;
552         pointer_moved = TRUE;
553         break;
554       default:
555         break;
556     }
557     g_mutex_lock (ximagesink->flow_lock);
558     g_mutex_lock (ximagesink->x_lock);
559   }
560
561   if (pointer_moved) {
562     g_mutex_unlock (ximagesink->x_lock);
563     g_mutex_unlock (ximagesink->flow_lock);
564
565     GST_DEBUG ("ximagesink pointer moved over window at %d,%d",
566         pointer_x, pointer_y);
567     gst_navigation_send_mouse_event (GST_NAVIGATION (ximagesink),
568         "mouse-move", 0, pointer_x, pointer_y);
569
570     g_mutex_lock (ximagesink->flow_lock);
571     g_mutex_lock (ximagesink->x_lock);
572   }
573
574   /* We get all remaining events on our window to throw them upstream */
575   while (XCheckWindowEvent (ximagesink->xcontext->disp,
576           ximagesink->xwindow->win,
577           KeyPressMask | KeyReleaseMask |
578           ButtonPressMask | ButtonReleaseMask, &e)) {
579     KeySym keysym;
580
581     /* We lock only for the X function call */
582     g_mutex_unlock (ximagesink->x_lock);
583     g_mutex_unlock (ximagesink->flow_lock);
584
585     switch (e.type) {
586       case ButtonPress:
587         /* Mouse button pressed/released over our window. We send upstream
588            events for interactivity/navigation */
589         GST_DEBUG ("ximagesink button %d pressed over window at %d,%d",
590             e.xbutton.button, e.xbutton.x, e.xbutton.x);
591         gst_navigation_send_mouse_event (GST_NAVIGATION (ximagesink),
592             "mouse-button-press", e.xbutton.button, e.xbutton.x, e.xbutton.y);
593         break;
594       case ButtonRelease:
595         GST_DEBUG ("ximagesink button %d release over window at %d,%d",
596             e.xbutton.button, e.xbutton.x, e.xbutton.x);
597         gst_navigation_send_mouse_event (GST_NAVIGATION (ximagesink),
598             "mouse-button-release", e.xbutton.button, e.xbutton.x, e.xbutton.y);
599         break;
600       case KeyPress:
601       case KeyRelease:
602         /* Key pressed/released over our window. We send upstream
603            events for interactivity/navigation */
604         GST_DEBUG ("ximagesink key %d pressed over window at %d,%d",
605             e.xkey.keycode, e.xkey.x, e.xkey.x);
606         g_mutex_lock (ximagesink->x_lock);
607         keysym = XKeycodeToKeysym (ximagesink->xcontext->disp,
608             e.xkey.keycode, 0);
609         g_mutex_unlock (ximagesink->x_lock);
610         if (keysym != NoSymbol) {
611           char *key_str = NULL;
612
613           g_mutex_lock (ximagesink->x_lock);
614           key_str = XKeysymToString (keysym);
615           g_mutex_unlock (ximagesink->x_lock);
616           gst_navigation_send_key_event (GST_NAVIGATION (ximagesink),
617               e.type == KeyPress ? "key-press" : "key-release", key_str);
618         } else {
619           gst_navigation_send_key_event (GST_NAVIGATION (ximagesink),
620               e.type == KeyPress ? "key-press" : "key-release", "unknown");
621         }
622         break;
623       default:
624         GST_DEBUG_OBJECT (ximagesink, "ximagesink unhandled X event (%d)",
625             e.type);
626     }
627     g_mutex_lock (ximagesink->flow_lock);
628     g_mutex_lock (ximagesink->x_lock);
629   }
630
631   /* Handle Expose */
632   while (XCheckWindowEvent (ximagesink->xcontext->disp,
633           ximagesink->xwindow->win, ExposureMask | StructureNotifyMask, &e)) {
634     switch (e.type) {
635       case Expose:
636         exposed = TRUE;
637         break;
638       case ConfigureNotify:
639         g_mutex_unlock (ximagesink->x_lock);
640         gst_ximagesink_xwindow_update_geometry (ximagesink);
641         g_mutex_lock (ximagesink->x_lock);
642         configured = TRUE;
643         break;
644       default:
645         break;
646     }
647   }
648
649   if (ximagesink->handle_expose && (exposed || configured)) {
650     g_mutex_unlock (ximagesink->x_lock);
651     g_mutex_unlock (ximagesink->flow_lock);
652
653     gst_ximagesink_expose (GST_X_OVERLAY (ximagesink));
654
655     g_mutex_lock (ximagesink->flow_lock);
656     g_mutex_lock (ximagesink->x_lock);
657   }
658
659   /* Handle Display events */
660   while (XPending (ximagesink->xcontext->disp)) {
661     XNextEvent (ximagesink->xcontext->disp, &e);
662
663     switch (e.type) {
664       case ClientMessage:{
665         Atom wm_delete;
666
667         wm_delete = XInternAtom (ximagesink->xcontext->disp,
668             "WM_DELETE_WINDOW", False);
669         if (wm_delete == (Atom) e.xclient.data.l[0]) {
670           /* Handle window deletion by posting an error on the bus */
671           GST_ELEMENT_ERROR (ximagesink, RESOURCE, NOT_FOUND,
672               ("Output window was closed"), (NULL));
673
674           g_mutex_unlock (ximagesink->x_lock);
675           gst_ximagesink_xwindow_destroy (ximagesink, ximagesink->xwindow);
676           ximagesink->xwindow = NULL;
677           g_mutex_lock (ximagesink->x_lock);
678         }
679         break;
680       }
681       default:
682         break;
683     }
684   }
685
686   g_mutex_unlock (ximagesink->x_lock);
687   g_mutex_unlock (ximagesink->flow_lock);
688 }
689
690 static gpointer
691 gst_ximagesink_event_thread (GstXImageSink * ximagesink)
692 {
693   g_return_val_if_fail (GST_IS_XIMAGESINK (ximagesink), NULL);
694
695   GST_OBJECT_LOCK (ximagesink);
696   while (ximagesink->running) {
697     GST_OBJECT_UNLOCK (ximagesink);
698
699     if (ximagesink->xwindow) {
700       gst_ximagesink_handle_xevents (ximagesink);
701     }
702     /* FIXME: do we want to align this with the framerate or anything else? */
703     g_usleep (G_USEC_PER_SEC / 20);
704
705     GST_OBJECT_LOCK (ximagesink);
706   }
707   GST_OBJECT_UNLOCK (ximagesink);
708
709   return NULL;
710 }
711
712 static void
713 gst_ximagesink_manage_event_thread (GstXImageSink * ximagesink)
714 {
715   GThread *thread = NULL;
716
717   /* don't start the thread too early */
718   if (ximagesink->xcontext == NULL) {
719     return;
720   }
721
722   GST_OBJECT_LOCK (ximagesink);
723   if (ximagesink->handle_expose || ximagesink->handle_events) {
724     if (!ximagesink->event_thread) {
725       /* Setup our event listening thread */
726       GST_DEBUG_OBJECT (ximagesink, "run xevent thread, expose %d, events %d",
727           ximagesink->handle_expose, ximagesink->handle_events);
728       ximagesink->running = TRUE;
729       ximagesink->event_thread = g_thread_create (
730           (GThreadFunc) gst_ximagesink_event_thread, ximagesink, TRUE, NULL);
731     }
732   } else {
733     if (ximagesink->event_thread) {
734       GST_DEBUG_OBJECT (ximagesink, "stop xevent thread, expose %d, events %d",
735           ximagesink->handle_expose, ximagesink->handle_events);
736       ximagesink->running = FALSE;
737       /* grab thread and mark it as NULL */
738       thread = ximagesink->event_thread;
739       ximagesink->event_thread = NULL;
740     }
741   }
742   GST_OBJECT_UNLOCK (ximagesink);
743
744   /* Wait for our event thread to finish */
745   if (thread)
746     g_thread_join (thread);
747
748 }
749
750
751 /* This function calculates the pixel aspect ratio based on the properties
752  * in the xcontext structure and stores it there. */
753 static void
754 gst_ximagesink_calculate_pixel_aspect_ratio (GstXContext * xcontext)
755 {
756   static const gint par[][2] = {
757     {1, 1},                     /* regular screen */
758     {16, 15},                   /* PAL TV */
759     {11, 10},                   /* 525 line Rec.601 video */
760     {54, 59},                   /* 625 line Rec.601 video */
761     {64, 45},                   /* 1280x1024 on 16:9 display */
762     {5, 3},                     /* 1280x1024 on 4:3 display */
763     {4, 3}                      /*  800x600 on 16:9 display */
764   };
765   gint i;
766   gint index;
767   gdouble ratio;
768   gdouble delta;
769
770 #define DELTA(idx) (ABS (ratio - ((gdouble) par[idx][0] / par[idx][1])))
771
772   /* first calculate the "real" ratio based on the X values;
773    * which is the "physical" w/h divided by the w/h in pixels of the display */
774   ratio = (gdouble) (xcontext->widthmm * xcontext->height)
775       / (xcontext->heightmm * xcontext->width);
776
777   /* DirectFB's X in 720x576 reports the physical dimensions wrong, so
778    * override here */
779   if (xcontext->width == 720 && xcontext->height == 576) {
780     ratio = 4.0 * 576 / (3.0 * 720);
781   }
782   GST_DEBUG ("calculated pixel aspect ratio: %f", ratio);
783
784   /* now find the one from par[][2] with the lowest delta to the real one */
785   delta = DELTA (0);
786   index = 0;
787
788   for (i = 1; i < sizeof (par) / (sizeof (gint) * 2); ++i) {
789     gdouble this_delta = DELTA (i);
790
791     if (this_delta < delta) {
792       index = i;
793       delta = this_delta;
794     }
795   }
796
797   GST_DEBUG ("Decided on index %d (%d/%d)", index,
798       par[index][0], par[index][1]);
799
800   g_free (xcontext->par);
801   xcontext->par = g_new0 (GValue, 1);
802   g_value_init (xcontext->par, GST_TYPE_FRACTION);
803   gst_value_set_fraction (xcontext->par, par[index][0], par[index][1]);
804   GST_DEBUG ("set xcontext PAR to %d/%d",
805       gst_value_get_fraction_numerator (xcontext->par),
806       gst_value_get_fraction_denominator (xcontext->par));
807 }
808
809 /* This function gets the X Display and global info about it. Everything is
810    stored in our object and will be cleaned when the object is disposed. Note
811    here that caps for supported format are generated without any window or
812    image creation */
813 static GstXContext *
814 gst_ximagesink_xcontext_get (GstXImageSink * ximagesink)
815 {
816   GstXContext *xcontext = NULL;
817   XPixmapFormatValues *px_formats = NULL;
818   gint nb_formats = 0, i;
819
820   g_return_val_if_fail (GST_IS_XIMAGESINK (ximagesink), NULL);
821
822   xcontext = g_new0 (GstXContext, 1);
823
824   g_mutex_lock (ximagesink->x_lock);
825
826   xcontext->disp = XOpenDisplay (ximagesink->display_name);
827
828   if (!xcontext->disp) {
829     g_mutex_unlock (ximagesink->x_lock);
830     g_free (xcontext);
831     GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
832         ("Could not initialise X output"), ("Could not open display"));
833     return NULL;
834   }
835
836   xcontext->screen = DefaultScreenOfDisplay (xcontext->disp);
837   xcontext->screen_num = DefaultScreen (xcontext->disp);
838   xcontext->visual = DefaultVisual (xcontext->disp, xcontext->screen_num);
839   xcontext->root = DefaultRootWindow (xcontext->disp);
840   xcontext->white = XWhitePixel (xcontext->disp, xcontext->screen_num);
841   xcontext->black = XBlackPixel (xcontext->disp, xcontext->screen_num);
842   xcontext->depth = DefaultDepthOfScreen (xcontext->screen);
843
844   xcontext->width = DisplayWidth (xcontext->disp, xcontext->screen_num);
845   xcontext->height = DisplayHeight (xcontext->disp, xcontext->screen_num);
846   xcontext->widthmm = DisplayWidthMM (xcontext->disp, xcontext->screen_num);
847   xcontext->heightmm = DisplayHeightMM (xcontext->disp, xcontext->screen_num);
848
849   GST_DEBUG_OBJECT (ximagesink, "X reports %dx%d pixels and %d mm x %d mm",
850       xcontext->width, xcontext->height, xcontext->widthmm, xcontext->heightmm);
851
852   gst_ximagesink_calculate_pixel_aspect_ratio (xcontext);
853
854   /* We get supported pixmap formats at supported depth */
855   px_formats = XListPixmapFormats (xcontext->disp, &nb_formats);
856
857   if (!px_formats) {
858     XCloseDisplay (xcontext->disp);
859     g_mutex_unlock (ximagesink->x_lock);
860     g_free (xcontext->par);
861     g_free (xcontext);
862     GST_ELEMENT_ERROR (ximagesink, RESOURCE, SETTINGS,
863         ("Could not get supported pixmap formats"), (NULL));
864     return NULL;
865   }
866
867   /* We get bpp value corresponding to our running depth */
868   for (i = 0; i < nb_formats; i++) {
869     if (px_formats[i].depth == xcontext->depth)
870       xcontext->bpp = px_formats[i].bits_per_pixel;
871   }
872
873   XFree (px_formats);
874
875   xcontext->endianness =
876       (ImageByteOrder (xcontext->disp) ==
877       LSBFirst) ? G_LITTLE_ENDIAN : G_BIG_ENDIAN;
878
879   /* Search for XShm extension support */
880 #ifdef HAVE_XSHM
881   if (XShmQueryExtension (xcontext->disp) &&
882       gst_ximagesink_check_xshm_calls (ximagesink, xcontext)) {
883     xcontext->use_xshm = TRUE;
884     GST_DEBUG ("ximagesink is using XShm extension");
885   } else
886 #endif /* HAVE_XSHM */
887   {
888     xcontext->use_xshm = FALSE;
889     GST_DEBUG ("ximagesink is not using XShm extension");
890   }
891
892   /* our caps system handles 24/32bpp RGB as big-endian. */
893   if ((xcontext->bpp == 24 || xcontext->bpp == 32) &&
894       xcontext->endianness == G_LITTLE_ENDIAN) {
895     xcontext->endianness = G_BIG_ENDIAN;
896     xcontext->visual->red_mask = GUINT32_TO_BE (xcontext->visual->red_mask);
897     xcontext->visual->green_mask = GUINT32_TO_BE (xcontext->visual->green_mask);
898     xcontext->visual->blue_mask = GUINT32_TO_BE (xcontext->visual->blue_mask);
899     if (xcontext->bpp == 24) {
900       xcontext->visual->red_mask >>= 8;
901       xcontext->visual->green_mask >>= 8;
902       xcontext->visual->blue_mask >>= 8;
903     }
904   }
905
906   /* update object's par with calculated one if not set yet */
907   if (!ximagesink->par) {
908     ximagesink->par = g_new0 (GValue, 1);
909     gst_value_init_and_copy (ximagesink->par, xcontext->par);
910     GST_DEBUG_OBJECT (ximagesink, "set calculated PAR on object's PAR");
911   }
912   xcontext->caps = gst_caps_new_simple ("video/x-raw-rgb",
913       "bpp", G_TYPE_INT, xcontext->bpp,
914       "depth", G_TYPE_INT, xcontext->depth,
915       "endianness", G_TYPE_INT, xcontext->endianness,
916       "red_mask", G_TYPE_INT, xcontext->visual->red_mask,
917       "green_mask", G_TYPE_INT, xcontext->visual->green_mask,
918       "blue_mask", G_TYPE_INT, xcontext->visual->blue_mask,
919       "width", GST_TYPE_INT_RANGE, 1, G_MAXINT,
920       "height", GST_TYPE_INT_RANGE, 1, G_MAXINT,
921       "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1, NULL);
922   if (ximagesink->par) {
923     int nom, den;
924
925     nom = gst_value_get_fraction_numerator (ximagesink->par);
926     den = gst_value_get_fraction_denominator (ximagesink->par);
927     gst_caps_set_simple (xcontext->caps, "pixel-aspect-ratio",
928         GST_TYPE_FRACTION, nom, den, NULL);
929   }
930
931   g_mutex_unlock (ximagesink->x_lock);
932
933   return xcontext;
934 }
935
936 /* This function cleans the X context. Closing the Display and unrefing the
937    caps for supported formats. */
938 static void
939 gst_ximagesink_xcontext_clear (GstXImageSink * ximagesink)
940 {
941   GstXContext *xcontext;
942
943   g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
944
945   GST_OBJECT_LOCK (ximagesink);
946   if (ximagesink->xcontext == NULL) {
947     GST_OBJECT_UNLOCK (ximagesink);
948     return;
949   }
950
951   /* Take the xcontext reference and NULL it while we
952    * clean it up, so that any buffer-alloced buffers 
953    * arriving after this will be freed correctly */
954   xcontext = ximagesink->xcontext;
955   ximagesink->xcontext = NULL;
956
957   GST_OBJECT_UNLOCK (ximagesink);
958
959   gst_caps_unref (xcontext->caps);
960   g_free (xcontext->par);
961   g_free (ximagesink->par);
962   ximagesink->par = NULL;
963
964   if (xcontext->last_caps)
965     gst_caps_replace (&xcontext->last_caps, NULL);
966
967   g_mutex_lock (ximagesink->x_lock);
968
969   XCloseDisplay (xcontext->disp);
970
971   g_mutex_unlock (ximagesink->x_lock);
972
973   g_free (xcontext);
974 }
975
976 /* Element stuff */
977
978 static GstCaps *
979 gst_ximagesink_getcaps (GstBaseSink * bsink, GstCaps * filter)
980 {
981   GstXImageSink *ximagesink;
982   GstCaps *caps;
983   int i;
984
985   ximagesink = GST_XIMAGESINK (bsink);
986
987   g_mutex_lock (ximagesink->x_lock);
988   if (ximagesink->xcontext) {
989     GstCaps *caps;
990
991     caps = gst_caps_ref (ximagesink->xcontext->caps);
992
993     if (filter) {
994       GstCaps *intersection;
995
996       intersection =
997           gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST);
998       gst_caps_unref (caps);
999       caps = intersection;
1000     }
1001
1002     if (ximagesink->xwindow && ximagesink->xwindow->width) {
1003       GstStructure *s0, *s1;
1004
1005       caps = gst_caps_make_writable (caps);
1006
1007       /* There can only be a single structure because the xcontext
1008        * caps only have a single structure */
1009       s0 = gst_caps_get_structure (caps, 0);
1010       s1 = gst_structure_copy (gst_caps_get_structure (caps, 0));
1011
1012       gst_structure_set (s0, "width", G_TYPE_INT, ximagesink->xwindow->width,
1013           "height", G_TYPE_INT, ximagesink->xwindow->height, NULL);
1014       gst_caps_append_structure (caps, s1);
1015
1016       /* This will not change the order but will remove the
1017        * fixed width/height caps again if not possible
1018        * upstream */
1019       if (filter) {
1020         GstCaps *intersection;
1021
1022         intersection =
1023             gst_caps_intersect_full (caps, filter, GST_CAPS_INTERSECT_FIRST);
1024         gst_caps_unref (caps);
1025         caps = intersection;
1026       }
1027     }
1028
1029     g_mutex_unlock (ximagesink->x_lock);
1030     return caps;
1031   }
1032   g_mutex_unlock (ximagesink->x_lock);
1033
1034   /* get a template copy and add the pixel aspect ratio */
1035   caps = gst_pad_get_pad_template_caps (GST_BASE_SINK (ximagesink)->sinkpad);
1036   if (ximagesink->par) {
1037     caps = gst_caps_make_writable (caps);
1038     for (i = 0; i < gst_caps_get_size (caps); ++i) {
1039       GstStructure *structure = gst_caps_get_structure (caps, i);
1040       int nom, den;
1041
1042       nom = gst_value_get_fraction_numerator (ximagesink->par);
1043       den = gst_value_get_fraction_denominator (ximagesink->par);
1044       gst_structure_set (structure, "pixel-aspect-ratio",
1045           GST_TYPE_FRACTION, nom, den, NULL);
1046     }
1047   }
1048
1049   if (filter) {
1050     GstCaps *intersection;
1051
1052     intersection =
1053         gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST);
1054     gst_caps_unref (caps);
1055     caps = intersection;
1056   }
1057
1058   return caps;
1059 }
1060
1061 static gboolean
1062 gst_ximagesink_setcaps (GstBaseSink * bsink, GstCaps * caps)
1063 {
1064   GstXImageSink *ximagesink;
1065   GstStructure *structure;
1066   GstBufferPool *newpool, *oldpool;
1067   gboolean ret = TRUE;
1068   const GValue *par;
1069   gint new_width, new_height;
1070   const GValue *fps;
1071
1072   ximagesink = GST_XIMAGESINK (bsink);
1073
1074   if (!ximagesink->xcontext)
1075     return FALSE;
1076
1077   GST_DEBUG_OBJECT (ximagesink,
1078       "sinkconnect possible caps %" GST_PTR_FORMAT " with given caps %"
1079       GST_PTR_FORMAT, ximagesink->xcontext->caps, caps);
1080
1081   /* We intersect those caps with our template to make sure they are correct */
1082   if (!gst_caps_can_intersect (ximagesink->xcontext->caps, caps))
1083     goto incompatible_caps;
1084
1085   structure = gst_caps_get_structure (caps, 0);
1086
1087   ret &= gst_structure_get_int (structure, "width", &new_width);
1088   ret &= gst_structure_get_int (structure, "height", &new_height);
1089   fps = gst_structure_get_value (structure, "framerate");
1090   ret &= (fps != NULL);
1091
1092   if (!ret)
1093     return FALSE;
1094
1095   /* if the caps contain pixel-aspect-ratio, they have to match ours,
1096    * otherwise linking should fail */
1097   par = gst_structure_get_value (structure, "pixel-aspect-ratio");
1098   if (par) {
1099     if (ximagesink->par) {
1100       if (gst_value_compare (par, ximagesink->par) != GST_VALUE_EQUAL) {
1101         goto wrong_aspect;
1102       }
1103     } else if (ximagesink->xcontext->par) {
1104       if (gst_value_compare (par, ximagesink->xcontext->par) != GST_VALUE_EQUAL) {
1105         goto wrong_aspect;
1106       }
1107     }
1108   }
1109
1110   GST_VIDEO_SINK_WIDTH (ximagesink) = new_width;
1111   GST_VIDEO_SINK_HEIGHT (ximagesink) = new_height;
1112   ximagesink->fps_n = gst_value_get_fraction_numerator (fps);
1113   ximagesink->fps_d = gst_value_get_fraction_denominator (fps);
1114
1115   /* Notify application to set xwindow id now */
1116   g_mutex_lock (ximagesink->flow_lock);
1117   if (!ximagesink->xwindow) {
1118     g_mutex_unlock (ximagesink->flow_lock);
1119     gst_x_overlay_prepare_xwindow_id (GST_X_OVERLAY (ximagesink));
1120   } else {
1121     g_mutex_unlock (ximagesink->flow_lock);
1122   }
1123
1124   /* Creating our window and our image */
1125   if (GST_VIDEO_SINK_WIDTH (ximagesink) <= 0 ||
1126       GST_VIDEO_SINK_HEIGHT (ximagesink) <= 0)
1127     goto invalid_size;
1128
1129   g_mutex_lock (ximagesink->flow_lock);
1130   if (!ximagesink->xwindow) {
1131     ximagesink->xwindow = gst_ximagesink_xwindow_new (ximagesink,
1132         GST_VIDEO_SINK_WIDTH (ximagesink), GST_VIDEO_SINK_HEIGHT (ximagesink));
1133   }
1134   /* Remember to draw borders for next frame */
1135   ximagesink->draw_border = TRUE;
1136
1137   /* create a new pool for the new configuration */
1138   newpool = gst_ximage_buffer_pool_new (ximagesink);
1139
1140   structure = gst_buffer_pool_get_config (newpool);
1141   gst_buffer_pool_config_set (structure, caps, 0, 0, 0, 0, 0, 16);
1142   if (!gst_buffer_pool_set_config (newpool, structure))
1143     goto config_failed;
1144
1145   if (!gst_buffer_pool_set_active (newpool, TRUE))
1146     goto activate_failed;
1147
1148   oldpool = ximagesink->pool;
1149   ximagesink->pool = newpool;
1150
1151   /* unref the old sink */
1152   if (oldpool) {
1153     /* deactivate */
1154     gst_buffer_pool_set_active (oldpool, FALSE);
1155     gst_object_unref (oldpool);
1156   }
1157   g_mutex_unlock (ximagesink->flow_lock);
1158
1159   return TRUE;
1160
1161   /* ERRORS */
1162 incompatible_caps:
1163   {
1164     GST_ERROR_OBJECT (ximagesink, "caps incompatible");
1165     return FALSE;
1166   }
1167 wrong_aspect:
1168   {
1169     GST_INFO_OBJECT (ximagesink, "pixel aspect ratio does not match");
1170     return FALSE;
1171   }
1172 invalid_size:
1173   {
1174     GST_ELEMENT_ERROR (ximagesink, CORE, NEGOTIATION, (NULL),
1175         ("Invalid image size."));
1176     return FALSE;
1177   }
1178 config_failed:
1179   {
1180     GST_ERROR_OBJECT (ximagesink, "failed to set config.");
1181     g_mutex_unlock (ximagesink->flow_lock);
1182     return FALSE;
1183   }
1184 activate_failed:
1185   {
1186     GST_ERROR_OBJECT (ximagesink, "failed to activate bufferpool.");
1187     g_mutex_unlock (ximagesink->flow_lock);
1188     gst_object_unref (newpool);
1189     return FALSE;
1190   }
1191 }
1192
1193 static GstStateChangeReturn
1194 gst_ximagesink_change_state (GstElement * element, GstStateChange transition)
1195 {
1196   GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
1197   GstXImageSink *ximagesink;
1198   GstXContext *xcontext = NULL;
1199
1200   ximagesink = GST_XIMAGESINK (element);
1201
1202   switch (transition) {
1203     case GST_STATE_CHANGE_NULL_TO_READY:
1204       /* Initializing the XContext */
1205       if (ximagesink->xcontext == NULL) {
1206         xcontext = gst_ximagesink_xcontext_get (ximagesink);
1207         if (xcontext == NULL) {
1208           ret = GST_STATE_CHANGE_FAILURE;
1209           goto beach;
1210         }
1211         GST_OBJECT_LOCK (ximagesink);
1212         if (xcontext)
1213           ximagesink->xcontext = xcontext;
1214         GST_OBJECT_UNLOCK (ximagesink);
1215       }
1216
1217       /* call XSynchronize with the current value of synchronous */
1218       GST_DEBUG_OBJECT (ximagesink, "XSynchronize called with %s",
1219           ximagesink->synchronous ? "TRUE" : "FALSE");
1220       g_mutex_lock (ximagesink->x_lock);
1221       XSynchronize (ximagesink->xcontext->disp, ximagesink->synchronous);
1222       g_mutex_unlock (ximagesink->x_lock);
1223       gst_ximagesink_manage_event_thread (ximagesink);
1224       break;
1225     case GST_STATE_CHANGE_READY_TO_PAUSED:
1226       g_mutex_lock (ximagesink->flow_lock);
1227       if (ximagesink->xwindow)
1228         gst_ximagesink_xwindow_clear (ximagesink, ximagesink->xwindow);
1229       g_mutex_unlock (ximagesink->flow_lock);
1230       break;
1231     case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
1232       break;
1233     default:
1234       break;
1235   }
1236
1237   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
1238
1239   switch (transition) {
1240     case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
1241       break;
1242     case GST_STATE_CHANGE_PAUSED_TO_READY:
1243       ximagesink->fps_n = 0;
1244       ximagesink->fps_d = 1;
1245       GST_VIDEO_SINK_WIDTH (ximagesink) = 0;
1246       GST_VIDEO_SINK_HEIGHT (ximagesink) = 0;
1247       g_mutex_lock (ximagesink->flow_lock);
1248       if (ximagesink->pool)
1249         gst_buffer_pool_set_active (ximagesink->pool, FALSE);
1250       g_mutex_unlock (ximagesink->flow_lock);
1251       break;
1252     case GST_STATE_CHANGE_READY_TO_NULL:
1253       gst_ximagesink_reset (ximagesink);
1254       break;
1255     default:
1256       break;
1257   }
1258
1259 beach:
1260   return ret;
1261 }
1262
1263 static void
1264 gst_ximagesink_get_times (GstBaseSink * bsink, GstBuffer * buf,
1265     GstClockTime * start, GstClockTime * end)
1266 {
1267   GstXImageSink *ximagesink;
1268
1269   ximagesink = GST_XIMAGESINK (bsink);
1270
1271   if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
1272     *start = GST_BUFFER_TIMESTAMP (buf);
1273     if (GST_BUFFER_DURATION_IS_VALID (buf)) {
1274       *end = *start + GST_BUFFER_DURATION (buf);
1275     } else {
1276       if (ximagesink->fps_n > 0) {
1277         *end = *start +
1278             gst_util_uint64_scale_int (GST_SECOND, ximagesink->fps_d,
1279             ximagesink->fps_n);
1280       }
1281     }
1282   }
1283 }
1284
1285 static GstFlowReturn
1286 gst_ximagesink_show_frame (GstVideoSink * vsink, GstBuffer * buf)
1287 {
1288   GstFlowReturn res;
1289   GstXImageSink *ximagesink;
1290   GstMetaXImage *meta;
1291   GstBuffer *temp;
1292   gboolean unref;
1293
1294   ximagesink = GST_XIMAGESINK (vsink);
1295
1296   meta = gst_buffer_get_meta_ximage (buf);
1297
1298   if (meta) {
1299     /* If this buffer has been allocated using our buffer management we simply
1300        put the ximage which is in the PRIVATE pointer */
1301     GST_LOG_OBJECT (ximagesink, "buffer from our pool, writing directly");
1302     res = GST_FLOW_OK;
1303     unref = FALSE;
1304   } else {
1305     guint8 *data;
1306     gsize size;
1307
1308     /* Else we have to copy the data into our private image, */
1309     /* if we have one... */
1310     GST_LOG_OBJECT (ximagesink, "buffer not from our pool, copying");
1311
1312     /* we should have a pool, configured in setcaps */
1313     if (ximagesink->pool == NULL)
1314       goto no_pool;
1315
1316     /* take a buffer form our pool */
1317     res = gst_buffer_pool_acquire_buffer (ximagesink->pool, &temp, NULL);
1318     if (res != GST_FLOW_OK)
1319       goto no_buffer;
1320
1321     unref = TRUE;
1322
1323     if (gst_buffer_get_size (temp) < gst_buffer_get_size (buf))
1324       goto wrong_size;
1325
1326     data = gst_buffer_map (temp, &size, NULL, GST_MAP_WRITE);
1327     gst_buffer_extract (buf, 0, data, size);
1328     gst_buffer_unmap (temp, data, size);
1329
1330     buf = temp;
1331   }
1332
1333   if (!gst_ximagesink_ximage_put (ximagesink, buf))
1334     goto no_window;
1335
1336 done:
1337   if (unref)
1338     gst_buffer_unref (buf);
1339
1340   return res;
1341
1342   /* ERRORS */
1343 no_pool:
1344   {
1345     GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
1346         ("Internal error: can't allocate images"),
1347         ("We don't have a bufferpool negotiated"));
1348     return GST_FLOW_ERROR;
1349   }
1350 no_buffer:
1351   {
1352     /* No image available. That's very bad ! */
1353     GST_WARNING_OBJECT (ximagesink, "could not create image");
1354     return res;
1355   }
1356 wrong_size:
1357   {
1358     GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
1359         ("Failed to create output image buffer"),
1360         ("XServer allocated buffer size did not match input buffer %"
1361             G_GSIZE_FORMAT " - %" G_GSIZE_FORMAT, gst_buffer_get_size (temp),
1362             gst_buffer_get_size (buf)));
1363     res = GST_FLOW_ERROR;
1364     goto done;
1365   }
1366 no_window:
1367   {
1368     /* No Window available to put our image into */
1369     GST_WARNING_OBJECT (ximagesink, "could not output image - no window");
1370     res = GST_FLOW_ERROR;
1371     goto done;
1372   }
1373 }
1374
1375 static gboolean
1376 gst_ximagesink_event (GstBaseSink * sink, GstEvent * event)
1377 {
1378   GstXImageSink *ximagesink = GST_XIMAGESINK (sink);
1379
1380   switch (GST_EVENT_TYPE (event)) {
1381     case GST_EVENT_TAG:{
1382       GstTagList *l;
1383       gchar *title = NULL;
1384
1385       gst_event_parse_tag (event, &l);
1386       gst_tag_list_get_string (l, GST_TAG_TITLE, &title);
1387
1388       if (title) {
1389         GST_DEBUG_OBJECT (ximagesink, "got tags, title='%s'", title);
1390         gst_ximagesink_xwindow_set_title (ximagesink, ximagesink->xwindow,
1391             title);
1392
1393         g_free (title);
1394       }
1395       break;
1396     }
1397     default:
1398       break;
1399   }
1400   if (GST_BASE_SINK_CLASS (parent_class)->event)
1401     return GST_BASE_SINK_CLASS (parent_class)->event (sink, event);
1402   else
1403     return TRUE;
1404 }
1405
1406 static gboolean
1407 gst_ximagesink_sink_query (GstPad * sinkpad, GstQuery * query)
1408 {
1409   GstXImageSink *ximagesink = GST_XIMAGESINK (GST_PAD_PARENT (sinkpad));
1410   gboolean res = TRUE;
1411
1412   switch (GST_QUERY_TYPE (query)) {
1413     case GST_QUERY_ALLOCATION:
1414     {
1415       GstBufferPool *pool;
1416       GstStructure *config;
1417       GstCaps *caps;
1418       guint size;
1419       gboolean need_pool;
1420
1421       gst_query_parse_allocation (query, &caps, &need_pool);
1422
1423       if (caps == NULL)
1424         goto no_caps;
1425
1426       g_mutex_lock (ximagesink->flow_lock);
1427       if ((pool = ximagesink->pool))
1428         gst_object_ref (pool);
1429       g_mutex_unlock (ximagesink->flow_lock);
1430
1431       if (pool != NULL) {
1432         const GstCaps *pcaps;
1433
1434         /* we had a pool, check caps */
1435         config = gst_buffer_pool_get_config (pool);
1436         gst_buffer_pool_config_get (config, &pcaps, &size, NULL, NULL, NULL,
1437             NULL, NULL);
1438
1439         GST_DEBUG_OBJECT (ximagesink,
1440             "we had a pool with caps %" GST_PTR_FORMAT, pcaps);
1441         if (!gst_caps_is_equal (caps, pcaps)) {
1442           /* different caps, we can't use this pool */
1443           GST_DEBUG_OBJECT (ximagesink, "pool has different caps");
1444           gst_object_unref (pool);
1445           pool = NULL;
1446         }
1447       }
1448       if (pool == NULL && need_pool) {
1449         GstVideoFormat format;
1450         gint width, height;
1451
1452         GST_DEBUG_OBJECT (ximagesink, "create new pool");
1453         pool = gst_ximage_buffer_pool_new (ximagesink);
1454
1455         if (!gst_video_format_parse_caps (caps, &format, &width, &height))
1456           goto invalid_caps;
1457
1458         /* the normal size of a frame */
1459         size = gst_video_format_get_size (format, width, height);
1460
1461         config = gst_buffer_pool_get_config (pool);
1462         gst_buffer_pool_config_set (config, caps, size, 0, 0, 0, 0, 16);
1463         if (!gst_buffer_pool_set_config (pool, config))
1464           goto config_failed;
1465       }
1466       gst_query_set_allocation_params (query, size, 0, 0, 0, 16, pool);
1467
1468       /* we also support various metadata */
1469       gst_query_add_allocation_meta (query, GST_META_API_VIDEO);
1470
1471       gst_object_unref (pool);
1472       break;
1473     }
1474     default:
1475       res = FALSE;
1476       break;
1477   }
1478   return res;
1479
1480   /* ERRORS */
1481 no_caps:
1482   {
1483     GST_DEBUG_OBJECT (sinkpad, "no caps specified");
1484     return FALSE;
1485   }
1486 invalid_caps:
1487   {
1488     GST_DEBUG_OBJECT (sinkpad, "invalid caps specified");
1489     return FALSE;
1490   }
1491 config_failed:
1492   {
1493     GST_DEBUG_OBJECT (sinkpad, "failed setting config");
1494     return FALSE;
1495   }
1496 }
1497
1498 /* Interfaces stuff */
1499
1500 static gboolean
1501 gst_ximagesink_interface_supported (GstImplementsInterface * iface, GType type)
1502 {
1503   if (type == GST_TYPE_NAVIGATION || type == GST_TYPE_X_OVERLAY)
1504     return TRUE;
1505   else
1506     return FALSE;
1507 }
1508
1509 static void
1510 gst_ximagesink_interface_init (GstImplementsInterfaceClass * klass)
1511 {
1512   klass->supported = gst_ximagesink_interface_supported;
1513 }
1514
1515 static void
1516 gst_ximagesink_navigation_send_event (GstNavigation * navigation,
1517     GstStructure * structure)
1518 {
1519   GstXImageSink *ximagesink = GST_XIMAGESINK (navigation);
1520   GstEvent *event;
1521   gint x_offset, y_offset;
1522   gdouble x, y;
1523   GstPad *pad = NULL;
1524
1525   event = gst_event_new_navigation (structure);
1526
1527   /* We are not converting the pointer coordinates as there's no hardware
1528      scaling done here. The only possible scaling is done by videoscale and
1529      videoscale will have to catch those events and tranform the coordinates
1530      to match the applied scaling. So here we just add the offset if the image
1531      is centered in the window.  */
1532
1533   /* We take the flow_lock while we look at the window */
1534   g_mutex_lock (ximagesink->flow_lock);
1535
1536   if (!ximagesink->xwindow) {
1537     g_mutex_unlock (ximagesink->flow_lock);
1538     return;
1539   }
1540
1541   x_offset = ximagesink->xwindow->width - GST_VIDEO_SINK_WIDTH (ximagesink);
1542   y_offset = ximagesink->xwindow->height - GST_VIDEO_SINK_HEIGHT (ximagesink);
1543
1544   g_mutex_unlock (ximagesink->flow_lock);
1545
1546   if (x_offset > 0 && gst_structure_get_double (structure, "pointer_x", &x)) {
1547     x -= x_offset / 2;
1548     gst_structure_set (structure, "pointer_x", G_TYPE_DOUBLE, x, NULL);
1549   }
1550   if (y_offset > 0 && gst_structure_get_double (structure, "pointer_y", &y)) {
1551     y -= y_offset / 2;
1552     gst_structure_set (structure, "pointer_y", G_TYPE_DOUBLE, y, NULL);
1553   }
1554
1555   pad = gst_pad_get_peer (GST_VIDEO_SINK_PAD (ximagesink));
1556
1557   if (GST_IS_PAD (pad) && GST_IS_EVENT (event)) {
1558     gst_pad_send_event (pad, event);
1559
1560     gst_object_unref (pad);
1561   }
1562 }
1563
1564 static void
1565 gst_ximagesink_navigation_init (GstNavigationInterface * iface)
1566 {
1567   iface->send_event = gst_ximagesink_navigation_send_event;
1568 }
1569
1570 static void
1571 gst_ximagesink_set_window_handle (GstXOverlay * overlay, guintptr id)
1572 {
1573   XID xwindow_id = id;
1574   GstXImageSink *ximagesink = GST_XIMAGESINK (overlay);
1575   GstXWindow *xwindow = NULL;
1576   XWindowAttributes attr;
1577
1578   /* We acquire the stream lock while setting this window in the element.
1579      We are basically cleaning tons of stuff replacing the old window, putting
1580      images while we do that would surely crash */
1581   g_mutex_lock (ximagesink->flow_lock);
1582
1583   /* If we already use that window return */
1584   if (ximagesink->xwindow && (xwindow_id == ximagesink->xwindow->win)) {
1585     g_mutex_unlock (ximagesink->flow_lock);
1586     return;
1587   }
1588
1589   /* If the element has not initialized the X11 context try to do so */
1590   if (!ximagesink->xcontext &&
1591       !(ximagesink->xcontext = gst_ximagesink_xcontext_get (ximagesink))) {
1592     g_mutex_unlock (ximagesink->flow_lock);
1593     /* we have thrown a GST_ELEMENT_ERROR now */
1594     return;
1595   }
1596
1597   /* If a window is there already we destroy it */
1598   if (ximagesink->xwindow) {
1599     gst_ximagesink_xwindow_destroy (ximagesink, ximagesink->xwindow);
1600     ximagesink->xwindow = NULL;
1601   }
1602
1603   /* If the xid is 0 we go back to an internal window */
1604   if (xwindow_id == 0) {
1605     /* If no width/height caps nego did not happen window will be created
1606        during caps nego then */
1607     if (GST_VIDEO_SINK_WIDTH (ximagesink) && GST_VIDEO_SINK_HEIGHT (ximagesink)) {
1608       xwindow = gst_ximagesink_xwindow_new (ximagesink,
1609           GST_VIDEO_SINK_WIDTH (ximagesink),
1610           GST_VIDEO_SINK_HEIGHT (ximagesink));
1611     }
1612   } else {
1613     xwindow = g_new0 (GstXWindow, 1);
1614
1615     xwindow->win = xwindow_id;
1616
1617     /* We get window geometry, set the event we want to receive,
1618        and create a GC */
1619     g_mutex_lock (ximagesink->x_lock);
1620     XGetWindowAttributes (ximagesink->xcontext->disp, xwindow->win, &attr);
1621     xwindow->width = attr.width;
1622     xwindow->height = attr.height;
1623     xwindow->internal = FALSE;
1624     if (ximagesink->handle_events) {
1625       XSelectInput (ximagesink->xcontext->disp, xwindow->win, ExposureMask |
1626           StructureNotifyMask | PointerMotionMask | KeyPressMask |
1627           KeyReleaseMask);
1628     }
1629
1630     xwindow->gc = XCreateGC (ximagesink->xcontext->disp, xwindow->win, 0, NULL);
1631     g_mutex_unlock (ximagesink->x_lock);
1632   }
1633
1634   if (xwindow)
1635     ximagesink->xwindow = xwindow;
1636
1637   g_mutex_unlock (ximagesink->flow_lock);
1638 }
1639
1640 static void
1641 gst_ximagesink_expose (GstXOverlay * overlay)
1642 {
1643   GstXImageSink *ximagesink = GST_XIMAGESINK (overlay);
1644
1645   gst_ximagesink_xwindow_update_geometry (ximagesink);
1646   gst_ximagesink_ximage_put (ximagesink, NULL);
1647 }
1648
1649 static void
1650 gst_ximagesink_set_event_handling (GstXOverlay * overlay,
1651     gboolean handle_events)
1652 {
1653   GstXImageSink *ximagesink = GST_XIMAGESINK (overlay);
1654
1655   ximagesink->handle_events = handle_events;
1656
1657   g_mutex_lock (ximagesink->flow_lock);
1658
1659   if (G_UNLIKELY (!ximagesink->xwindow)) {
1660     g_mutex_unlock (ximagesink->flow_lock);
1661     return;
1662   }
1663
1664   g_mutex_lock (ximagesink->x_lock);
1665
1666   if (handle_events) {
1667     if (ximagesink->xwindow->internal) {
1668       XSelectInput (ximagesink->xcontext->disp, ximagesink->xwindow->win,
1669           ExposureMask | StructureNotifyMask | PointerMotionMask |
1670           KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask);
1671     } else {
1672       XSelectInput (ximagesink->xcontext->disp, ximagesink->xwindow->win,
1673           ExposureMask | StructureNotifyMask | PointerMotionMask |
1674           KeyPressMask | KeyReleaseMask);
1675     }
1676   } else {
1677     XSelectInput (ximagesink->xcontext->disp, ximagesink->xwindow->win, 0);
1678   }
1679
1680   g_mutex_unlock (ximagesink->x_lock);
1681
1682   g_mutex_unlock (ximagesink->flow_lock);
1683 }
1684
1685 static void
1686 gst_ximagesink_xoverlay_init (GstXOverlayClass * iface)
1687 {
1688   iface->set_window_handle = gst_ximagesink_set_window_handle;
1689   iface->expose = gst_ximagesink_expose;
1690   iface->handle_events = gst_ximagesink_set_event_handling;
1691 }
1692
1693 /* =========================================== */
1694 /*                                             */
1695 /*              Init & Class init              */
1696 /*                                             */
1697 /* =========================================== */
1698
1699 static void
1700 gst_ximagesink_set_property (GObject * object, guint prop_id,
1701     const GValue * value, GParamSpec * pspec)
1702 {
1703   GstXImageSink *ximagesink;
1704
1705   g_return_if_fail (GST_IS_XIMAGESINK (object));
1706
1707   ximagesink = GST_XIMAGESINK (object);
1708
1709   switch (prop_id) {
1710     case PROP_DISPLAY:
1711       ximagesink->display_name = g_strdup (g_value_get_string (value));
1712       break;
1713     case PROP_SYNCHRONOUS:
1714       ximagesink->synchronous = g_value_get_boolean (value);
1715       if (ximagesink->xcontext) {
1716         GST_DEBUG_OBJECT (ximagesink, "XSynchronize called with %s",
1717             ximagesink->synchronous ? "TRUE" : "FALSE");
1718         g_mutex_lock (ximagesink->x_lock);
1719         XSynchronize (ximagesink->xcontext->disp, ximagesink->synchronous);
1720         g_mutex_unlock (ximagesink->x_lock);
1721       }
1722       break;
1723     case PROP_FORCE_ASPECT_RATIO:
1724       ximagesink->keep_aspect = g_value_get_boolean (value);
1725       break;
1726     case PROP_PIXEL_ASPECT_RATIO:
1727     {
1728       GValue *tmp;
1729
1730       tmp = g_new0 (GValue, 1);
1731       g_value_init (tmp, GST_TYPE_FRACTION);
1732
1733       if (!g_value_transform (value, tmp)) {
1734         GST_WARNING_OBJECT (ximagesink,
1735             "Could not transform string to aspect ratio");
1736         g_free (tmp);
1737       } else {
1738         GST_DEBUG_OBJECT (ximagesink, "set PAR to %d/%d",
1739             gst_value_get_fraction_numerator (tmp),
1740             gst_value_get_fraction_denominator (tmp));
1741         g_free (ximagesink->par);
1742         ximagesink->par = tmp;
1743       }
1744     }
1745       break;
1746     case PROP_HANDLE_EVENTS:
1747       gst_ximagesink_set_event_handling (GST_X_OVERLAY (ximagesink),
1748           g_value_get_boolean (value));
1749       gst_ximagesink_manage_event_thread (ximagesink);
1750       break;
1751     case PROP_HANDLE_EXPOSE:
1752       ximagesink->handle_expose = g_value_get_boolean (value);
1753       gst_ximagesink_manage_event_thread (ximagesink);
1754       break;
1755     default:
1756       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1757       break;
1758   }
1759 }
1760
1761 static void
1762 gst_ximagesink_get_property (GObject * object, guint prop_id,
1763     GValue * value, GParamSpec * pspec)
1764 {
1765   GstXImageSink *ximagesink;
1766
1767   g_return_if_fail (GST_IS_XIMAGESINK (object));
1768
1769   ximagesink = GST_XIMAGESINK (object);
1770
1771   switch (prop_id) {
1772     case PROP_DISPLAY:
1773       g_value_set_string (value, ximagesink->display_name);
1774       break;
1775     case PROP_SYNCHRONOUS:
1776       g_value_set_boolean (value, ximagesink->synchronous);
1777       break;
1778     case PROP_FORCE_ASPECT_RATIO:
1779       g_value_set_boolean (value, ximagesink->keep_aspect);
1780       break;
1781     case PROP_PIXEL_ASPECT_RATIO:
1782       if (ximagesink->par)
1783         g_value_transform (ximagesink->par, value);
1784       break;
1785     case PROP_HANDLE_EVENTS:
1786       g_value_set_boolean (value, ximagesink->handle_events);
1787       break;
1788     case PROP_HANDLE_EXPOSE:
1789       g_value_set_boolean (value, ximagesink->handle_expose);
1790       break;
1791     case PROP_WINDOW_WIDTH:
1792       if (ximagesink->xwindow)
1793         g_value_set_uint64 (value, ximagesink->xwindow->width);
1794       else
1795         g_value_set_uint64 (value, 0);
1796       break;
1797     case PROP_WINDOW_HEIGHT:
1798       if (ximagesink->xwindow)
1799         g_value_set_uint64 (value, ximagesink->xwindow->height);
1800       else
1801         g_value_set_uint64 (value, 0);
1802       break;
1803     default:
1804       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1805       break;
1806   }
1807 }
1808
1809 static void
1810 gst_ximagesink_reset (GstXImageSink * ximagesink)
1811 {
1812   GThread *thread;
1813
1814   GST_OBJECT_LOCK (ximagesink);
1815   ximagesink->running = FALSE;
1816   /* grab thread and mark it as NULL */
1817   thread = ximagesink->event_thread;
1818   ximagesink->event_thread = NULL;
1819   GST_OBJECT_UNLOCK (ximagesink);
1820
1821   /* Wait for our event thread to finish before we clean up our stuff. */
1822   if (thread)
1823     g_thread_join (thread);
1824
1825   if (ximagesink->cur_image) {
1826     gst_buffer_unref (ximagesink->cur_image);
1827     ximagesink->cur_image = NULL;
1828   }
1829
1830   g_mutex_lock (ximagesink->flow_lock);
1831
1832   if (ximagesink->pool) {
1833     gst_object_unref (ximagesink->pool);
1834     ximagesink->pool = NULL;
1835   }
1836
1837   if (ximagesink->xwindow) {
1838     gst_ximagesink_xwindow_clear (ximagesink, ximagesink->xwindow);
1839     gst_ximagesink_xwindow_destroy (ximagesink, ximagesink->xwindow);
1840     ximagesink->xwindow = NULL;
1841   }
1842   g_mutex_unlock (ximagesink->flow_lock);
1843
1844   gst_ximagesink_xcontext_clear (ximagesink);
1845 }
1846
1847 static void
1848 gst_ximagesink_finalize (GObject * object)
1849 {
1850   GstXImageSink *ximagesink;
1851
1852   ximagesink = GST_XIMAGESINK (object);
1853
1854   gst_ximagesink_reset (ximagesink);
1855
1856   if (ximagesink->display_name) {
1857     g_free (ximagesink->display_name);
1858     ximagesink->display_name = NULL;
1859   }
1860   if (ximagesink->par) {
1861     g_free (ximagesink->par);
1862     ximagesink->par = NULL;
1863   }
1864   if (ximagesink->x_lock) {
1865     g_mutex_free (ximagesink->x_lock);
1866     ximagesink->x_lock = NULL;
1867   }
1868   if (ximagesink->flow_lock) {
1869     g_mutex_free (ximagesink->flow_lock);
1870     ximagesink->flow_lock = NULL;
1871   }
1872
1873   g_free (ximagesink->media_title);
1874
1875   G_OBJECT_CLASS (parent_class)->finalize (object);
1876 }
1877
1878 static void
1879 gst_ximagesink_init (GstXImageSink * ximagesink)
1880 {
1881   /* for the ALLOCATION query */
1882   gst_pad_set_query_function (GST_BASE_SINK (ximagesink)->sinkpad,
1883       gst_ximagesink_sink_query);
1884
1885   ximagesink->display_name = NULL;
1886   ximagesink->xcontext = NULL;
1887   ximagesink->xwindow = NULL;
1888   ximagesink->cur_image = NULL;
1889
1890   ximagesink->event_thread = NULL;
1891   ximagesink->running = FALSE;
1892
1893   ximagesink->fps_n = 0;
1894   ximagesink->fps_d = 1;
1895
1896   ximagesink->x_lock = g_mutex_new ();
1897   ximagesink->flow_lock = g_mutex_new ();
1898
1899   ximagesink->par = NULL;
1900
1901   ximagesink->pool = NULL;
1902
1903   ximagesink->synchronous = FALSE;
1904   ximagesink->keep_aspect = FALSE;
1905   ximagesink->handle_events = TRUE;
1906   ximagesink->handle_expose = TRUE;
1907 }
1908
1909 static void
1910 gst_ximagesink_class_init (GstXImageSinkClass * klass)
1911 {
1912   GObjectClass *gobject_class;
1913   GstElementClass *gstelement_class;
1914   GstBaseSinkClass *gstbasesink_class;
1915   GstVideoSinkClass *videosink_class;
1916
1917   gobject_class = (GObjectClass *) klass;
1918   gstelement_class = (GstElementClass *) klass;
1919   gstbasesink_class = (GstBaseSinkClass *) klass;
1920   videosink_class = (GstVideoSinkClass *) klass;
1921
1922   gobject_class->finalize = gst_ximagesink_finalize;
1923   gobject_class->set_property = gst_ximagesink_set_property;
1924   gobject_class->get_property = gst_ximagesink_get_property;
1925
1926   g_object_class_install_property (gobject_class, PROP_DISPLAY,
1927       g_param_spec_string ("display", "Display", "X Display name",
1928           NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1929   g_object_class_install_property (gobject_class, PROP_SYNCHRONOUS,
1930       g_param_spec_boolean ("synchronous", "Synchronous", "When enabled, runs "
1931           "the X display in synchronous mode. (used only for debugging)", FALSE,
1932           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1933   g_object_class_install_property (gobject_class, PROP_FORCE_ASPECT_RATIO,
1934       g_param_spec_boolean ("force-aspect-ratio", "Force aspect ratio",
1935           "When enabled, reverse caps negotiation (scaling) will respect "
1936           "original aspect ratio", FALSE,
1937           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1938   g_object_class_install_property (gobject_class, PROP_PIXEL_ASPECT_RATIO,
1939       g_param_spec_string ("pixel-aspect-ratio", "Pixel Aspect Ratio",
1940           "The pixel aspect ratio of the device", "1/1",
1941           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1942   g_object_class_install_property (gobject_class, PROP_HANDLE_EVENTS,
1943       g_param_spec_boolean ("handle-events", "Handle XEvents",
1944           "When enabled, XEvents will be selected and handled", TRUE,
1945           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1946   g_object_class_install_property (gobject_class, PROP_HANDLE_EXPOSE,
1947       g_param_spec_boolean ("handle-expose", "Handle expose",
1948           "When enabled, "
1949           "the current frame will always be drawn in response to X Expose "
1950           "events", TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1951
1952   /**
1953    * GstXImageSink:window-width
1954    *
1955    * Actual width of the video window.
1956    *
1957    * Since: 0.10.32
1958    */
1959   g_object_class_install_property (gobject_class, PROP_WINDOW_WIDTH,
1960       g_param_spec_uint64 ("window-width", "window-width",
1961           "Width of the window", 0, G_MAXUINT64, 0,
1962           G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
1963
1964   /**
1965    * GstXImageSink:window-height
1966    *
1967    * Actual height of the video window.
1968    *
1969    * Since: 0.10.32
1970    */
1971   g_object_class_install_property (gobject_class, PROP_WINDOW_HEIGHT,
1972       g_param_spec_uint64 ("window-height", "window-height",
1973           "Height of the window", 0, G_MAXUINT64, 0,
1974           G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
1975
1976   gst_element_class_set_details_simple (gstelement_class,
1977       "Video sink", "Sink/Video",
1978       "A standard X based videosink", "Julien Moutte <julien@moutte.net>");
1979
1980   gst_element_class_add_pad_template (gstelement_class,
1981       gst_static_pad_template_get (&gst_ximagesink_sink_template_factory));
1982
1983   gstelement_class->change_state = gst_ximagesink_change_state;
1984
1985   gstbasesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_ximagesink_getcaps);
1986   gstbasesink_class->set_caps = GST_DEBUG_FUNCPTR (gst_ximagesink_setcaps);
1987   gstbasesink_class->get_times = GST_DEBUG_FUNCPTR (gst_ximagesink_get_times);
1988   gstbasesink_class->event = GST_DEBUG_FUNCPTR (gst_ximagesink_event);
1989
1990   videosink_class->show_frame = GST_DEBUG_FUNCPTR (gst_ximagesink_show_frame);
1991 }