ximagesink: Update for negotiation related API 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, GstCaps * filter)
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     GstCaps *caps;
988
989     caps = gst_caps_ref (ximagesink->xcontext->caps);
990
991     if (filter) {
992       GstCaps *intersection;
993
994       intersection =
995           gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST);
996       gst_caps_unref (caps);
997       caps = intersection;
998     }
999
1000     if (ximagesink->xwindow && ximagesink->xwindow->width) {
1001       GstStructure *s0, *s1;
1002
1003       caps = gst_caps_make_writable (caps);
1004
1005       /* There can only be a single structure because the xcontext
1006        * caps only have a single structure */
1007       s0 = gst_caps_get_structure (caps, 0);
1008       s1 = gst_structure_copy (gst_caps_get_structure (caps, 0));
1009
1010       gst_structure_set (s0, "width", G_TYPE_INT, ximagesink->xwindow->width,
1011           "height", G_TYPE_INT, ximagesink->xwindow->height, NULL);
1012       gst_caps_append_structure (caps, s1);
1013
1014       /* This will not change the order but will remove the
1015        * fixed width/height caps again if not possible
1016        * upstream */
1017       if (filter) {
1018         GstCaps *intersection;
1019
1020         intersection =
1021             gst_caps_intersect_full (caps, filter, GST_CAPS_INTERSECT_FIRST);
1022         gst_caps_unref (caps);
1023         caps = intersection;
1024       }
1025     }
1026
1027     g_mutex_unlock (ximagesink->x_lock);
1028     return caps;
1029   }
1030   g_mutex_unlock (ximagesink->x_lock);
1031
1032   /* get a template copy and add the pixel aspect ratio */
1033   caps =
1034       gst_caps_copy (gst_pad_get_pad_template_caps (GST_BASE_SINK
1035           (ximagesink)->sinkpad));
1036   for (i = 0; i < gst_caps_get_size (caps); ++i) {
1037     GstStructure *structure = gst_caps_get_structure (caps, i);
1038
1039     if (ximagesink->par) {
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     return FALSE;
1189   }
1190 }
1191
1192 static GstStateChangeReturn
1193 gst_ximagesink_change_state (GstElement * element, GstStateChange transition)
1194 {
1195   GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
1196   GstXImageSink *ximagesink;
1197   GstXContext *xcontext = NULL;
1198
1199   ximagesink = GST_XIMAGESINK (element);
1200
1201   switch (transition) {
1202     case GST_STATE_CHANGE_NULL_TO_READY:
1203       /* Initializing the XContext */
1204       if (ximagesink->xcontext == NULL) {
1205         xcontext = gst_ximagesink_xcontext_get (ximagesink);
1206         if (xcontext == NULL) {
1207           ret = GST_STATE_CHANGE_FAILURE;
1208           goto beach;
1209         }
1210         GST_OBJECT_LOCK (ximagesink);
1211         if (xcontext)
1212           ximagesink->xcontext = xcontext;
1213         GST_OBJECT_UNLOCK (ximagesink);
1214       }
1215
1216       /* call XSynchronize with the current value of synchronous */
1217       GST_DEBUG_OBJECT (ximagesink, "XSynchronize called with %s",
1218           ximagesink->synchronous ? "TRUE" : "FALSE");
1219       g_mutex_lock (ximagesink->x_lock);
1220       XSynchronize (ximagesink->xcontext->disp, ximagesink->synchronous);
1221       g_mutex_unlock (ximagesink->x_lock);
1222       gst_ximagesink_manage_event_thread (ximagesink);
1223       break;
1224     case GST_STATE_CHANGE_READY_TO_PAUSED:
1225       g_mutex_lock (ximagesink->flow_lock);
1226       if (ximagesink->xwindow)
1227         gst_ximagesink_xwindow_clear (ximagesink, ximagesink->xwindow);
1228       g_mutex_unlock (ximagesink->flow_lock);
1229       break;
1230     case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
1231       break;
1232     default:
1233       break;
1234   }
1235
1236   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
1237
1238   switch (transition) {
1239     case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
1240       break;
1241     case GST_STATE_CHANGE_PAUSED_TO_READY:
1242       ximagesink->fps_n = 0;
1243       ximagesink->fps_d = 1;
1244       GST_VIDEO_SINK_WIDTH (ximagesink) = 0;
1245       GST_VIDEO_SINK_HEIGHT (ximagesink) = 0;
1246       g_mutex_lock (ximagesink->flow_lock);
1247       if (ximagesink->pool)
1248         gst_buffer_pool_set_active (ximagesink->pool, FALSE);
1249       g_mutex_unlock (ximagesink->flow_lock);
1250       break;
1251     case GST_STATE_CHANGE_READY_TO_NULL:
1252       gst_ximagesink_reset (ximagesink);
1253       break;
1254     default:
1255       break;
1256   }
1257
1258 beach:
1259   return ret;
1260 }
1261
1262 static void
1263 gst_ximagesink_get_times (GstBaseSink * bsink, GstBuffer * buf,
1264     GstClockTime * start, GstClockTime * end)
1265 {
1266   GstXImageSink *ximagesink;
1267
1268   ximagesink = GST_XIMAGESINK (bsink);
1269
1270   if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
1271     *start = GST_BUFFER_TIMESTAMP (buf);
1272     if (GST_BUFFER_DURATION_IS_VALID (buf)) {
1273       *end = *start + GST_BUFFER_DURATION (buf);
1274     } else {
1275       if (ximagesink->fps_n > 0) {
1276         *end = *start +
1277             gst_util_uint64_scale_int (GST_SECOND, ximagesink->fps_d,
1278             ximagesink->fps_n);
1279       }
1280     }
1281   }
1282 }
1283
1284 static GstFlowReturn
1285 gst_ximagesink_show_frame (GstVideoSink * vsink, GstBuffer * buf)
1286 {
1287   GstFlowReturn res;
1288   GstXImageSink *ximagesink;
1289   GstMetaXImage *meta;
1290   GstBuffer *temp;
1291   gboolean unref;
1292
1293   ximagesink = GST_XIMAGESINK (vsink);
1294
1295   meta = gst_buffer_get_meta_ximage (buf);
1296
1297   if (meta) {
1298     /* If this buffer has been allocated using our buffer management we simply
1299        put the ximage which is in the PRIVATE pointer */
1300     GST_LOG_OBJECT (ximagesink, "buffer from our pool, writing directly");
1301     res = GST_FLOW_OK;
1302     unref = FALSE;
1303   } else {
1304     guint8 *data;
1305     gsize size;
1306
1307     /* Else we have to copy the data into our private image, */
1308     /* if we have one... */
1309     GST_LOG_OBJECT (ximagesink, "buffer not from our pool, copying");
1310
1311     /* we should have a pool, configured in setcaps */
1312     if (ximagesink->pool == NULL)
1313       goto no_pool;
1314
1315     /* take a buffer form our pool */
1316     res = gst_buffer_pool_acquire_buffer (ximagesink->pool, &temp, NULL);
1317     if (res != GST_FLOW_OK)
1318       goto no_buffer;
1319
1320     unref = TRUE;
1321
1322     if (gst_buffer_get_size (temp) < gst_buffer_get_size (buf))
1323       goto wrong_size;
1324
1325     data = gst_buffer_map (temp, &size, NULL, GST_MAP_WRITE);
1326     gst_buffer_extract (buf, 0, data, size);
1327     gst_buffer_unmap (temp, data, size);
1328
1329     buf = temp;
1330   }
1331
1332   if (!gst_ximagesink_ximage_put (ximagesink, buf))
1333     goto no_window;
1334
1335 done:
1336   if (unref)
1337     gst_buffer_unref (buf);
1338
1339   return res;
1340
1341   /* ERRORS */
1342 no_pool:
1343   {
1344     GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
1345         ("Internal error: can't allocate images"),
1346         ("We don't have a bufferpool negotiated"));
1347     return GST_FLOW_ERROR;
1348   }
1349 no_buffer:
1350   {
1351     /* No image available. That's very bad ! */
1352     GST_WARNING_OBJECT (ximagesink, "could not create image");
1353     return res;
1354   }
1355 wrong_size:
1356   {
1357     GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
1358         ("Failed to create output image buffer"),
1359         ("XServer allocated buffer size did not match input buffer %"
1360             G_GSIZE_FORMAT " - %" G_GSIZE_FORMAT, gst_buffer_get_size (temp),
1361             gst_buffer_get_size (buf)));
1362     res = GST_FLOW_ERROR;
1363     goto done;
1364   }
1365 no_window:
1366   {
1367     /* No Window available to put our image into */
1368     GST_WARNING_OBJECT (ximagesink, "could not output image - no window");
1369     res = GST_FLOW_ERROR;
1370     goto done;
1371   }
1372 }
1373
1374 static gboolean
1375 gst_ximagesink_event (GstBaseSink * sink, GstEvent * event)
1376 {
1377   GstXImageSink *ximagesink = GST_XIMAGESINK (sink);
1378
1379   switch (GST_EVENT_TYPE (event)) {
1380     case GST_EVENT_TAG:{
1381       GstTagList *l;
1382       gchar *title = NULL;
1383
1384       gst_event_parse_tag (event, &l);
1385       gst_tag_list_get_string (l, GST_TAG_TITLE, &title);
1386
1387       if (title) {
1388         GST_DEBUG_OBJECT (ximagesink, "got tags, title='%s'", title);
1389         gst_ximagesink_xwindow_set_title (ximagesink, ximagesink->xwindow,
1390             title);
1391
1392         g_free (title);
1393       }
1394       break;
1395     }
1396     default:
1397       break;
1398   }
1399   if (GST_BASE_SINK_CLASS (parent_class)->event)
1400     return GST_BASE_SINK_CLASS (parent_class)->event (sink, event);
1401   else
1402     return TRUE;
1403 }
1404
1405 /* Interfaces stuff */
1406
1407 static gboolean
1408 gst_ximagesink_interface_supported (GstImplementsInterface * iface, GType type)
1409 {
1410   g_assert (type == GST_TYPE_NAVIGATION || type == GST_TYPE_X_OVERLAY);
1411   return TRUE;
1412 }
1413
1414 static void
1415 gst_ximagesink_interface_init (GstImplementsInterfaceClass * klass)
1416 {
1417   klass->supported = gst_ximagesink_interface_supported;
1418 }
1419
1420 static void
1421 gst_ximagesink_navigation_send_event (GstNavigation * navigation,
1422     GstStructure * structure)
1423 {
1424   GstXImageSink *ximagesink = GST_XIMAGESINK (navigation);
1425   GstEvent *event;
1426   gint x_offset, y_offset;
1427   gdouble x, y;
1428   GstPad *pad = NULL;
1429
1430   event = gst_event_new_navigation (structure);
1431
1432   /* We are not converting the pointer coordinates as there's no hardware
1433      scaling done here. The only possible scaling is done by videoscale and
1434      videoscale will have to catch those events and tranform the coordinates
1435      to match the applied scaling. So here we just add the offset if the image
1436      is centered in the window.  */
1437
1438   /* We take the flow_lock while we look at the window */
1439   g_mutex_lock (ximagesink->flow_lock);
1440
1441   if (!ximagesink->xwindow) {
1442     g_mutex_unlock (ximagesink->flow_lock);
1443     return;
1444   }
1445
1446   x_offset = ximagesink->xwindow->width - GST_VIDEO_SINK_WIDTH (ximagesink);
1447   y_offset = ximagesink->xwindow->height - GST_VIDEO_SINK_HEIGHT (ximagesink);
1448
1449   g_mutex_unlock (ximagesink->flow_lock);
1450
1451   if (x_offset > 0 && gst_structure_get_double (structure, "pointer_x", &x)) {
1452     x -= x_offset / 2;
1453     gst_structure_set (structure, "pointer_x", G_TYPE_DOUBLE, x, NULL);
1454   }
1455   if (y_offset > 0 && gst_structure_get_double (structure, "pointer_y", &y)) {
1456     y -= y_offset / 2;
1457     gst_structure_set (structure, "pointer_y", G_TYPE_DOUBLE, y, NULL);
1458   }
1459
1460   pad = gst_pad_get_peer (GST_VIDEO_SINK_PAD (ximagesink));
1461
1462   if (GST_IS_PAD (pad) && GST_IS_EVENT (event)) {
1463     gst_pad_send_event (pad, event);
1464
1465     gst_object_unref (pad);
1466   }
1467 }
1468
1469 static void
1470 gst_ximagesink_navigation_init (GstNavigationInterface * iface)
1471 {
1472   iface->send_event = gst_ximagesink_navigation_send_event;
1473 }
1474
1475 static void
1476 gst_ximagesink_set_window_handle (GstXOverlay * overlay, guintptr id)
1477 {
1478   XID xwindow_id = id;
1479   GstXImageSink *ximagesink = GST_XIMAGESINK (overlay);
1480   GstXWindow *xwindow = NULL;
1481   XWindowAttributes attr;
1482
1483   /* We acquire the stream lock while setting this window in the element.
1484      We are basically cleaning tons of stuff replacing the old window, putting
1485      images while we do that would surely crash */
1486   g_mutex_lock (ximagesink->flow_lock);
1487
1488   /* If we already use that window return */
1489   if (ximagesink->xwindow && (xwindow_id == ximagesink->xwindow->win)) {
1490     g_mutex_unlock (ximagesink->flow_lock);
1491     return;
1492   }
1493
1494   /* If the element has not initialized the X11 context try to do so */
1495   if (!ximagesink->xcontext &&
1496       !(ximagesink->xcontext = gst_ximagesink_xcontext_get (ximagesink))) {
1497     g_mutex_unlock (ximagesink->flow_lock);
1498     /* we have thrown a GST_ELEMENT_ERROR now */
1499     return;
1500   }
1501
1502   /* If a window is there already we destroy it */
1503   if (ximagesink->xwindow) {
1504     gst_ximagesink_xwindow_destroy (ximagesink, ximagesink->xwindow);
1505     ximagesink->xwindow = NULL;
1506   }
1507
1508   /* If the xid is 0 we go back to an internal window */
1509   if (xwindow_id == 0) {
1510     /* If no width/height caps nego did not happen window will be created
1511        during caps nego then */
1512     if (GST_VIDEO_SINK_WIDTH (ximagesink) && GST_VIDEO_SINK_HEIGHT (ximagesink)) {
1513       xwindow = gst_ximagesink_xwindow_new (ximagesink,
1514           GST_VIDEO_SINK_WIDTH (ximagesink),
1515           GST_VIDEO_SINK_HEIGHT (ximagesink));
1516     }
1517   } else {
1518     xwindow = g_new0 (GstXWindow, 1);
1519
1520     xwindow->win = xwindow_id;
1521
1522     /* We get window geometry, set the event we want to receive,
1523        and create a GC */
1524     g_mutex_lock (ximagesink->x_lock);
1525     XGetWindowAttributes (ximagesink->xcontext->disp, xwindow->win, &attr);
1526     xwindow->width = attr.width;
1527     xwindow->height = attr.height;
1528     xwindow->internal = FALSE;
1529     if (ximagesink->handle_events) {
1530       XSelectInput (ximagesink->xcontext->disp, xwindow->win, ExposureMask |
1531           StructureNotifyMask | PointerMotionMask | KeyPressMask |
1532           KeyReleaseMask);
1533     }
1534
1535     xwindow->gc = XCreateGC (ximagesink->xcontext->disp, xwindow->win, 0, NULL);
1536     g_mutex_unlock (ximagesink->x_lock);
1537   }
1538
1539   if (xwindow)
1540     ximagesink->xwindow = xwindow;
1541
1542   g_mutex_unlock (ximagesink->flow_lock);
1543 }
1544
1545 static void
1546 gst_ximagesink_expose (GstXOverlay * overlay)
1547 {
1548   GstXImageSink *ximagesink = GST_XIMAGESINK (overlay);
1549
1550   gst_ximagesink_xwindow_update_geometry (ximagesink);
1551   gst_ximagesink_ximage_put (ximagesink, NULL);
1552 }
1553
1554 static void
1555 gst_ximagesink_set_event_handling (GstXOverlay * overlay,
1556     gboolean handle_events)
1557 {
1558   GstXImageSink *ximagesink = GST_XIMAGESINK (overlay);
1559
1560   ximagesink->handle_events = handle_events;
1561
1562   g_mutex_lock (ximagesink->flow_lock);
1563
1564   if (G_UNLIKELY (!ximagesink->xwindow)) {
1565     g_mutex_unlock (ximagesink->flow_lock);
1566     return;
1567   }
1568
1569   g_mutex_lock (ximagesink->x_lock);
1570
1571   if (handle_events) {
1572     if (ximagesink->xwindow->internal) {
1573       XSelectInput (ximagesink->xcontext->disp, ximagesink->xwindow->win,
1574           ExposureMask | StructureNotifyMask | PointerMotionMask |
1575           KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask);
1576     } else {
1577       XSelectInput (ximagesink->xcontext->disp, ximagesink->xwindow->win,
1578           ExposureMask | StructureNotifyMask | PointerMotionMask |
1579           KeyPressMask | KeyReleaseMask);
1580     }
1581   } else {
1582     XSelectInput (ximagesink->xcontext->disp, ximagesink->xwindow->win, 0);
1583   }
1584
1585   g_mutex_unlock (ximagesink->x_lock);
1586
1587   g_mutex_unlock (ximagesink->flow_lock);
1588 }
1589
1590 static void
1591 gst_ximagesink_xoverlay_init (GstXOverlayClass * iface)
1592 {
1593   iface->set_window_handle = gst_ximagesink_set_window_handle;
1594   iface->expose = gst_ximagesink_expose;
1595   iface->handle_events = gst_ximagesink_set_event_handling;
1596 }
1597
1598 /* =========================================== */
1599 /*                                             */
1600 /*              Init & Class init              */
1601 /*                                             */
1602 /* =========================================== */
1603
1604 static void
1605 gst_ximagesink_set_property (GObject * object, guint prop_id,
1606     const GValue * value, GParamSpec * pspec)
1607 {
1608   GstXImageSink *ximagesink;
1609
1610   g_return_if_fail (GST_IS_XIMAGESINK (object));
1611
1612   ximagesink = GST_XIMAGESINK (object);
1613
1614   switch (prop_id) {
1615     case PROP_DISPLAY:
1616       ximagesink->display_name = g_strdup (g_value_get_string (value));
1617       break;
1618     case PROP_SYNCHRONOUS:
1619       ximagesink->synchronous = g_value_get_boolean (value);
1620       if (ximagesink->xcontext) {
1621         GST_DEBUG_OBJECT (ximagesink, "XSynchronize called with %s",
1622             ximagesink->synchronous ? "TRUE" : "FALSE");
1623         g_mutex_lock (ximagesink->x_lock);
1624         XSynchronize (ximagesink->xcontext->disp, ximagesink->synchronous);
1625         g_mutex_unlock (ximagesink->x_lock);
1626       }
1627       break;
1628     case PROP_FORCE_ASPECT_RATIO:
1629       ximagesink->keep_aspect = g_value_get_boolean (value);
1630       break;
1631     case PROP_PIXEL_ASPECT_RATIO:
1632     {
1633       GValue *tmp;
1634
1635       tmp = g_new0 (GValue, 1);
1636       g_value_init (tmp, GST_TYPE_FRACTION);
1637
1638       if (!g_value_transform (value, tmp)) {
1639         GST_WARNING_OBJECT (ximagesink,
1640             "Could not transform string to aspect ratio");
1641         g_free (tmp);
1642       } else {
1643         GST_DEBUG_OBJECT (ximagesink, "set PAR to %d/%d",
1644             gst_value_get_fraction_numerator (tmp),
1645             gst_value_get_fraction_denominator (tmp));
1646         g_free (ximagesink->par);
1647         ximagesink->par = tmp;
1648       }
1649     }
1650       break;
1651     case PROP_HANDLE_EVENTS:
1652       gst_ximagesink_set_event_handling (GST_X_OVERLAY (ximagesink),
1653           g_value_get_boolean (value));
1654       gst_ximagesink_manage_event_thread (ximagesink);
1655       break;
1656     case PROP_HANDLE_EXPOSE:
1657       ximagesink->handle_expose = g_value_get_boolean (value);
1658       gst_ximagesink_manage_event_thread (ximagesink);
1659       break;
1660     default:
1661       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1662       break;
1663   }
1664 }
1665
1666 static void
1667 gst_ximagesink_get_property (GObject * object, guint prop_id,
1668     GValue * value, GParamSpec * pspec)
1669 {
1670   GstXImageSink *ximagesink;
1671
1672   g_return_if_fail (GST_IS_XIMAGESINK (object));
1673
1674   ximagesink = GST_XIMAGESINK (object);
1675
1676   switch (prop_id) {
1677     case PROP_DISPLAY:
1678       g_value_set_string (value, ximagesink->display_name);
1679       break;
1680     case PROP_SYNCHRONOUS:
1681       g_value_set_boolean (value, ximagesink->synchronous);
1682       break;
1683     case PROP_FORCE_ASPECT_RATIO:
1684       g_value_set_boolean (value, ximagesink->keep_aspect);
1685       break;
1686     case PROP_PIXEL_ASPECT_RATIO:
1687       if (ximagesink->par)
1688         g_value_transform (ximagesink->par, value);
1689       break;
1690     case PROP_HANDLE_EVENTS:
1691       g_value_set_boolean (value, ximagesink->handle_events);
1692       break;
1693     case PROP_HANDLE_EXPOSE:
1694       g_value_set_boolean (value, ximagesink->handle_expose);
1695       break;
1696     case PROP_WINDOW_WIDTH:
1697       if (ximagesink->xwindow)
1698         g_value_set_uint64 (value, ximagesink->xwindow->width);
1699       else
1700         g_value_set_uint64 (value, 0);
1701       break;
1702     case PROP_WINDOW_HEIGHT:
1703       if (ximagesink->xwindow)
1704         g_value_set_uint64 (value, ximagesink->xwindow->height);
1705       else
1706         g_value_set_uint64 (value, 0);
1707       break;
1708     default:
1709       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1710       break;
1711   }
1712 }
1713
1714 static void
1715 gst_ximagesink_reset (GstXImageSink * ximagesink)
1716 {
1717   GThread *thread;
1718
1719   GST_OBJECT_LOCK (ximagesink);
1720   ximagesink->running = FALSE;
1721   /* grab thread and mark it as NULL */
1722   thread = ximagesink->event_thread;
1723   ximagesink->event_thread = NULL;
1724   GST_OBJECT_UNLOCK (ximagesink);
1725
1726   /* Wait for our event thread to finish before we clean up our stuff. */
1727   if (thread)
1728     g_thread_join (thread);
1729
1730   if (ximagesink->cur_image) {
1731     gst_buffer_unref (ximagesink->cur_image);
1732     ximagesink->cur_image = NULL;
1733   }
1734
1735   g_mutex_lock (ximagesink->flow_lock);
1736
1737   if (ximagesink->pool) {
1738     gst_object_unref (ximagesink->pool);
1739     ximagesink->pool = NULL;
1740   }
1741
1742   if (ximagesink->xwindow) {
1743     gst_ximagesink_xwindow_clear (ximagesink, ximagesink->xwindow);
1744     gst_ximagesink_xwindow_destroy (ximagesink, ximagesink->xwindow);
1745     ximagesink->xwindow = NULL;
1746   }
1747   g_mutex_unlock (ximagesink->flow_lock);
1748
1749   gst_ximagesink_xcontext_clear (ximagesink);
1750 }
1751
1752 static void
1753 gst_ximagesink_finalize (GObject * object)
1754 {
1755   GstXImageSink *ximagesink;
1756
1757   ximagesink = GST_XIMAGESINK (object);
1758
1759   gst_ximagesink_reset (ximagesink);
1760
1761   if (ximagesink->display_name) {
1762     g_free (ximagesink->display_name);
1763     ximagesink->display_name = NULL;
1764   }
1765   if (ximagesink->par) {
1766     g_free (ximagesink->par);
1767     ximagesink->par = NULL;
1768   }
1769   if (ximagesink->x_lock) {
1770     g_mutex_free (ximagesink->x_lock);
1771     ximagesink->x_lock = NULL;
1772   }
1773   if (ximagesink->flow_lock) {
1774     g_mutex_free (ximagesink->flow_lock);
1775     ximagesink->flow_lock = NULL;
1776   }
1777
1778   g_free (ximagesink->media_title);
1779
1780   G_OBJECT_CLASS (parent_class)->finalize (object);
1781 }
1782
1783 static void
1784 gst_ximagesink_init (GstXImageSink * ximagesink)
1785 {
1786   ximagesink->display_name = NULL;
1787   ximagesink->xcontext = NULL;
1788   ximagesink->xwindow = NULL;
1789   ximagesink->cur_image = NULL;
1790
1791   ximagesink->event_thread = NULL;
1792   ximagesink->running = FALSE;
1793
1794   ximagesink->fps_n = 0;
1795   ximagesink->fps_d = 1;
1796
1797   ximagesink->x_lock = g_mutex_new ();
1798   ximagesink->flow_lock = g_mutex_new ();
1799
1800   ximagesink->par = NULL;
1801
1802   ximagesink->pool = NULL;
1803
1804   ximagesink->synchronous = FALSE;
1805   ximagesink->keep_aspect = FALSE;
1806   ximagesink->handle_events = TRUE;
1807   ximagesink->handle_expose = TRUE;
1808 }
1809
1810 static void
1811 gst_ximagesink_class_init (GstXImageSinkClass * klass)
1812 {
1813   GObjectClass *gobject_class;
1814   GstElementClass *gstelement_class;
1815   GstBaseSinkClass *gstbasesink_class;
1816   GstVideoSinkClass *videosink_class;
1817
1818   gobject_class = (GObjectClass *) klass;
1819   gstelement_class = (GstElementClass *) klass;
1820   gstbasesink_class = (GstBaseSinkClass *) klass;
1821   videosink_class = (GstVideoSinkClass *) klass;
1822
1823   gobject_class->finalize = gst_ximagesink_finalize;
1824   gobject_class->set_property = gst_ximagesink_set_property;
1825   gobject_class->get_property = gst_ximagesink_get_property;
1826
1827   g_object_class_install_property (gobject_class, PROP_DISPLAY,
1828       g_param_spec_string ("display", "Display", "X Display name",
1829           NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1830   g_object_class_install_property (gobject_class, PROP_SYNCHRONOUS,
1831       g_param_spec_boolean ("synchronous", "Synchronous", "When enabled, runs "
1832           "the X display in synchronous mode. (used only for debugging)", FALSE,
1833           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1834   g_object_class_install_property (gobject_class, PROP_FORCE_ASPECT_RATIO,
1835       g_param_spec_boolean ("force-aspect-ratio", "Force aspect ratio",
1836           "When enabled, reverse caps negotiation (scaling) will respect "
1837           "original aspect ratio", FALSE,
1838           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1839   g_object_class_install_property (gobject_class, PROP_PIXEL_ASPECT_RATIO,
1840       g_param_spec_string ("pixel-aspect-ratio", "Pixel Aspect Ratio",
1841           "The pixel aspect ratio of the device", "1/1",
1842           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1843   g_object_class_install_property (gobject_class, PROP_HANDLE_EVENTS,
1844       g_param_spec_boolean ("handle-events", "Handle XEvents",
1845           "When enabled, XEvents will be selected and handled", TRUE,
1846           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1847   g_object_class_install_property (gobject_class, PROP_HANDLE_EXPOSE,
1848       g_param_spec_boolean ("handle-expose", "Handle expose",
1849           "When enabled, "
1850           "the current frame will always be drawn in response to X Expose "
1851           "events", TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1852
1853   /**
1854    * GstXImageSink:window-width
1855    *
1856    * Actual width of the video window.
1857    *
1858    * Since: 0.10.32
1859    */
1860   g_object_class_install_property (gobject_class, PROP_WINDOW_WIDTH,
1861       g_param_spec_uint64 ("window-width", "window-width",
1862           "Width of the window", 0, G_MAXUINT64, 0,
1863           G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
1864
1865   /**
1866    * GstXImageSink:window-height
1867    *
1868    * Actual height of the video window.
1869    *
1870    * Since: 0.10.32
1871    */
1872   g_object_class_install_property (gobject_class, PROP_WINDOW_HEIGHT,
1873       g_param_spec_uint64 ("window-height", "window-height",
1874           "Height of the window", 0, G_MAXUINT64, 0,
1875           G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
1876
1877   gst_element_class_set_details_simple (gstelement_class,
1878       "Video sink", "Sink/Video",
1879       "A standard X based videosink", "Julien Moutte <julien@moutte.net>");
1880
1881   gst_element_class_add_pad_template (gstelement_class,
1882       gst_static_pad_template_get (&gst_ximagesink_sink_template_factory));
1883
1884   gstelement_class->change_state = gst_ximagesink_change_state;
1885
1886   gstbasesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_ximagesink_getcaps);
1887   gstbasesink_class->set_caps = GST_DEBUG_FUNCPTR (gst_ximagesink_setcaps);
1888   gstbasesink_class->get_times = GST_DEBUG_FUNCPTR (gst_ximagesink_get_times);
1889   gstbasesink_class->event = GST_DEBUG_FUNCPTR (gst_ximagesink_event);
1890
1891   videosink_class->show_frame = GST_DEBUG_FUNCPTR (gst_ximagesink_show_frame);
1892 }