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