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