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