1540d962d5ff27927021a5e8108c9bab69eda057
[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, 15, 0, 0, };
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     GstBufferPoolAcquireParams params = { 0, };
1316
1317     /* Else we have to copy the data into our private image, */
1318     /* if we have one... */
1319     GST_LOG_OBJECT (ximagesink, "buffer not from our pool, copying");
1320
1321     /* we should have a pool, configured in setcaps */
1322     if (ximagesink->pool == NULL)
1323       goto no_pool;
1324
1325     if (!gst_buffer_pool_set_active (ximagesink->pool, TRUE))
1326       goto activate_failed;
1327
1328     /* take a buffer from our pool, if there is no buffer in the pool something
1329      * is seriously wrong, waiting for the pool here might deadlock when we try
1330      * to go to PAUSED because we never flush the pool. */
1331     params.flags = GST_BUFFER_POOL_ACQUIRE_FLAG_DONTWAIT;
1332     res = gst_buffer_pool_acquire_buffer (ximagesink->pool, &to_put, &params);
1333     if (res != GST_FLOW_OK)
1334       goto no_buffer;
1335
1336     GST_CAT_LOG_OBJECT (GST_CAT_PERFORMANCE, ximagesink,
1337         "slow copy into bufferpool buffer %p", to_put);
1338
1339     if (!gst_video_frame_map (&src, &ximagesink->info, buf, GST_MAP_READ))
1340       goto invalid_buffer;
1341
1342     if (!gst_video_frame_map (&dest, &ximagesink->info, to_put, GST_MAP_WRITE)) {
1343       gst_video_frame_unmap (&src);
1344       goto invalid_buffer;
1345     }
1346
1347     gst_video_frame_copy (&dest, &src);
1348
1349     gst_video_frame_unmap (&dest);
1350     gst_video_frame_unmap (&src);
1351   }
1352
1353   if (!gst_ximagesink_ximage_put (ximagesink, to_put))
1354     goto no_window;
1355
1356 done:
1357   if (to_put != buf)
1358     gst_buffer_unref (to_put);
1359
1360   return res;
1361
1362   /* ERRORS */
1363 no_pool:
1364   {
1365     GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
1366         ("Internal error: can't allocate images"),
1367         ("We don't have a bufferpool negotiated"));
1368     return GST_FLOW_ERROR;
1369   }
1370 no_buffer:
1371   {
1372     /* No image available. That's very bad ! */
1373     GST_WARNING_OBJECT (ximagesink, "could not create image");
1374     return GST_FLOW_OK;
1375   }
1376 invalid_buffer:
1377   {
1378     /* No Window available to put our image into */
1379     GST_WARNING_OBJECT (ximagesink, "could not map image");
1380     res = GST_FLOW_OK;
1381     goto done;
1382   }
1383 no_window:
1384   {
1385     /* No Window available to put our image into */
1386     GST_WARNING_OBJECT (ximagesink, "could not output image - no window");
1387     res = GST_FLOW_ERROR;
1388     goto done;
1389   }
1390 activate_failed:
1391   {
1392     GST_ERROR_OBJECT (ximagesink, "failed to activate bufferpool.");
1393     res = GST_FLOW_ERROR;
1394     goto done;
1395   }
1396 }
1397
1398 static gboolean
1399 gst_ximagesink_event (GstBaseSink * sink, GstEvent * event)
1400 {
1401   GstXImageSink *ximagesink = GST_XIMAGESINK (sink);
1402
1403   switch (GST_EVENT_TYPE (event)) {
1404     case GST_EVENT_TAG:{
1405       GstTagList *l;
1406       gchar *title = NULL;
1407
1408       gst_event_parse_tag (event, &l);
1409       gst_tag_list_get_string (l, GST_TAG_TITLE, &title);
1410
1411       if (title) {
1412         GST_DEBUG_OBJECT (ximagesink, "got tags, title='%s'", title);
1413         gst_ximagesink_xwindow_set_title (ximagesink, ximagesink->xwindow,
1414             title);
1415
1416         g_free (title);
1417       }
1418       break;
1419     }
1420     default:
1421       break;
1422   }
1423   return GST_BASE_SINK_CLASS (parent_class)->event (sink, event);
1424 }
1425
1426 static gboolean
1427 gst_ximagesink_propose_allocation (GstBaseSink * bsink, GstQuery * query)
1428 {
1429   GstXImageSink *ximagesink = GST_XIMAGESINK (bsink);
1430   GstBufferPool *pool;
1431   GstStructure *config;
1432   GstCaps *caps;
1433   guint size;
1434   gboolean need_pool;
1435
1436   gst_query_parse_allocation (query, &caps, &need_pool);
1437
1438   if (caps == NULL)
1439     goto no_caps;
1440
1441   g_mutex_lock (ximagesink->flow_lock);
1442   if ((pool = ximagesink->pool))
1443     gst_object_ref (pool);
1444   g_mutex_unlock (ximagesink->flow_lock);
1445
1446   if (pool != NULL) {
1447     GstCaps *pcaps;
1448
1449     /* we had a pool, check caps */
1450     config = gst_buffer_pool_get_config (pool);
1451     gst_buffer_pool_config_get_params (config, &pcaps, &size, NULL, NULL);
1452
1453     GST_DEBUG_OBJECT (ximagesink,
1454         "we had a pool with caps %" GST_PTR_FORMAT, pcaps);
1455     if (!gst_caps_is_equal (caps, pcaps)) {
1456       /* different caps, we can't use this pool */
1457       GST_DEBUG_OBJECT (ximagesink, "pool has different caps");
1458       gst_object_unref (pool);
1459       pool = NULL;
1460     }
1461     gst_structure_free (config);
1462   }
1463   if (pool == NULL && need_pool) {
1464     GstVideoInfo info;
1465
1466     if (!gst_video_info_from_caps (&info, caps))
1467       goto invalid_caps;
1468
1469     GST_DEBUG_OBJECT (ximagesink, "create new pool");
1470     pool = gst_ximage_buffer_pool_new (ximagesink);
1471
1472     /* the normal size of a frame */
1473     size = info.size;
1474
1475     config = gst_buffer_pool_get_config (pool);
1476     gst_buffer_pool_config_set_params (config, caps, size, 0, 0);
1477     if (!gst_buffer_pool_set_config (pool, config))
1478       goto config_failed;
1479   }
1480   if (pool) {
1481     /* we need at least 2 buffer because we hold on to the last one */
1482     gst_query_add_allocation_pool (query, pool, size, 2, 0);
1483     gst_object_unref (pool);
1484   }
1485
1486   /* we also support various metadata */
1487   gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL);
1488   gst_query_add_allocation_meta (query, GST_VIDEO_CROP_META_API_TYPE, NULL);
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     gst_object_unref (pool);
1507     return FALSE;
1508   }
1509 }
1510
1511 /* Interfaces stuff */
1512 static void
1513 gst_ximagesink_navigation_send_event (GstNavigation * navigation,
1514     GstStructure * structure)
1515 {
1516   GstXImageSink *ximagesink = GST_XIMAGESINK (navigation);
1517   GstEvent *event;
1518   gint x_offset, y_offset;
1519   gdouble x, y;
1520   GstPad *pad = NULL;
1521
1522   event = gst_event_new_navigation (structure);
1523
1524   /* We are not converting the pointer coordinates as there's no hardware
1525      scaling done here. The only possible scaling is done by videoscale and
1526      videoscale will have to catch those events and tranform the coordinates
1527      to match the applied scaling. So here we just add the offset if the image
1528      is centered in the window.  */
1529
1530   /* We take the flow_lock while we look at the window */
1531   g_mutex_lock (ximagesink->flow_lock);
1532
1533   if (!ximagesink->xwindow) {
1534     g_mutex_unlock (ximagesink->flow_lock);
1535     return;
1536   }
1537
1538   x_offset = ximagesink->xwindow->width - GST_VIDEO_SINK_WIDTH (ximagesink);
1539   y_offset = ximagesink->xwindow->height - GST_VIDEO_SINK_HEIGHT (ximagesink);
1540
1541   g_mutex_unlock (ximagesink->flow_lock);
1542
1543   if (x_offset > 0 && gst_structure_get_double (structure, "pointer_x", &x)) {
1544     x -= x_offset / 2;
1545     gst_structure_set (structure, "pointer_x", G_TYPE_DOUBLE, x, NULL);
1546   }
1547   if (y_offset > 0 && gst_structure_get_double (structure, "pointer_y", &y)) {
1548     y -= y_offset / 2;
1549     gst_structure_set (structure, "pointer_y", G_TYPE_DOUBLE, y, NULL);
1550   }
1551
1552   pad = gst_pad_get_peer (GST_VIDEO_SINK_PAD (ximagesink));
1553
1554   if (GST_IS_PAD (pad) && GST_IS_EVENT (event)) {
1555     gst_pad_send_event (pad, event);
1556
1557     gst_object_unref (pad);
1558   }
1559 }
1560
1561 static void
1562 gst_ximagesink_navigation_init (GstNavigationInterface * iface)
1563 {
1564   iface->send_event = gst_ximagesink_navigation_send_event;
1565 }
1566
1567 static void
1568 gst_ximagesink_set_window_handle (GstVideoOverlay * overlay, guintptr id)
1569 {
1570   XID xwindow_id = id;
1571   GstXImageSink *ximagesink = GST_XIMAGESINK (overlay);
1572   GstXWindow *xwindow = NULL;
1573   XWindowAttributes attr;
1574
1575   /* We acquire the stream lock while setting this window in the element.
1576      We are basically cleaning tons of stuff replacing the old window, putting
1577      images while we do that would surely crash */
1578   g_mutex_lock (ximagesink->flow_lock);
1579
1580   /* If we already use that window return */
1581   if (ximagesink->xwindow && (xwindow_id == ximagesink->xwindow->win)) {
1582     g_mutex_unlock (ximagesink->flow_lock);
1583     return;
1584   }
1585
1586   /* If the element has not initialized the X11 context try to do so */
1587   if (!ximagesink->xcontext &&
1588       !(ximagesink->xcontext = gst_ximagesink_xcontext_get (ximagesink))) {
1589     g_mutex_unlock (ximagesink->flow_lock);
1590     /* we have thrown a GST_ELEMENT_ERROR now */
1591     return;
1592   }
1593
1594   /* If a window is there already we destroy it */
1595   if (ximagesink->xwindow) {
1596     gst_ximagesink_xwindow_destroy (ximagesink, ximagesink->xwindow);
1597     ximagesink->xwindow = NULL;
1598   }
1599
1600   /* If the xid is 0 we go back to an internal window */
1601   if (xwindow_id == 0) {
1602     /* If no width/height caps nego did not happen window will be created
1603        during caps nego then */
1604     if (GST_VIDEO_SINK_WIDTH (ximagesink) && GST_VIDEO_SINK_HEIGHT (ximagesink)) {
1605       xwindow = gst_ximagesink_xwindow_new (ximagesink,
1606           GST_VIDEO_SINK_WIDTH (ximagesink),
1607           GST_VIDEO_SINK_HEIGHT (ximagesink));
1608     }
1609   } else {
1610     xwindow = g_new0 (GstXWindow, 1);
1611
1612     xwindow->win = xwindow_id;
1613
1614     /* We get window geometry, set the event we want to receive,
1615        and create a GC */
1616     g_mutex_lock (ximagesink->x_lock);
1617     XGetWindowAttributes (ximagesink->xcontext->disp, xwindow->win, &attr);
1618     xwindow->width = attr.width;
1619     xwindow->height = attr.height;
1620     xwindow->internal = FALSE;
1621     if (ximagesink->handle_events) {
1622       XSelectInput (ximagesink->xcontext->disp, xwindow->win, ExposureMask |
1623           StructureNotifyMask | PointerMotionMask | KeyPressMask |
1624           KeyReleaseMask);
1625     }
1626
1627     xwindow->gc = XCreateGC (ximagesink->xcontext->disp, xwindow->win, 0, NULL);
1628     g_mutex_unlock (ximagesink->x_lock);
1629   }
1630
1631   if (xwindow)
1632     ximagesink->xwindow = xwindow;
1633
1634   g_mutex_unlock (ximagesink->flow_lock);
1635 }
1636
1637 static void
1638 gst_ximagesink_expose (GstVideoOverlay * overlay)
1639 {
1640   GstXImageSink *ximagesink = GST_XIMAGESINK (overlay);
1641
1642   gst_ximagesink_xwindow_update_geometry (ximagesink);
1643   gst_ximagesink_ximage_put (ximagesink, NULL);
1644 }
1645
1646 static void
1647 gst_ximagesink_set_event_handling (GstVideoOverlay * overlay,
1648     gboolean handle_events)
1649 {
1650   GstXImageSink *ximagesink = GST_XIMAGESINK (overlay);
1651
1652   ximagesink->handle_events = handle_events;
1653
1654   g_mutex_lock (ximagesink->flow_lock);
1655
1656   if (G_UNLIKELY (!ximagesink->xwindow)) {
1657     g_mutex_unlock (ximagesink->flow_lock);
1658     return;
1659   }
1660
1661   g_mutex_lock (ximagesink->x_lock);
1662
1663   if (handle_events) {
1664     if (ximagesink->xwindow->internal) {
1665       XSelectInput (ximagesink->xcontext->disp, ximagesink->xwindow->win,
1666           ExposureMask | StructureNotifyMask | PointerMotionMask |
1667           KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask);
1668     } else {
1669       XSelectInput (ximagesink->xcontext->disp, ximagesink->xwindow->win,
1670           ExposureMask | StructureNotifyMask | PointerMotionMask |
1671           KeyPressMask | KeyReleaseMask);
1672     }
1673   } else {
1674     XSelectInput (ximagesink->xcontext->disp, ximagesink->xwindow->win, 0);
1675   }
1676
1677   g_mutex_unlock (ximagesink->x_lock);
1678
1679   g_mutex_unlock (ximagesink->flow_lock);
1680 }
1681
1682 static void
1683 gst_ximagesink_video_overlay_init (GstVideoOverlayInterface * iface)
1684 {
1685   iface->set_window_handle = gst_ximagesink_set_window_handle;
1686   iface->expose = gst_ximagesink_expose;
1687   iface->handle_events = gst_ximagesink_set_event_handling;
1688 }
1689
1690 /* =========================================== */
1691 /*                                             */
1692 /*              Init & Class init              */
1693 /*                                             */
1694 /* =========================================== */
1695
1696 static void
1697 gst_ximagesink_set_property (GObject * object, guint prop_id,
1698     const GValue * value, GParamSpec * pspec)
1699 {
1700   GstXImageSink *ximagesink;
1701
1702   g_return_if_fail (GST_IS_XIMAGESINK (object));
1703
1704   ximagesink = GST_XIMAGESINK (object);
1705
1706   switch (prop_id) {
1707     case PROP_DISPLAY:
1708       ximagesink->display_name = g_strdup (g_value_get_string (value));
1709       break;
1710     case PROP_SYNCHRONOUS:
1711       ximagesink->synchronous = g_value_get_boolean (value);
1712       if (ximagesink->xcontext) {
1713         GST_DEBUG_OBJECT (ximagesink, "XSynchronize called with %s",
1714             ximagesink->synchronous ? "TRUE" : "FALSE");
1715         g_mutex_lock (ximagesink->x_lock);
1716         XSynchronize (ximagesink->xcontext->disp, ximagesink->synchronous);
1717         g_mutex_unlock (ximagesink->x_lock);
1718       }
1719       break;
1720     case PROP_FORCE_ASPECT_RATIO:
1721       ximagesink->keep_aspect = g_value_get_boolean (value);
1722       break;
1723     case PROP_PIXEL_ASPECT_RATIO:
1724     {
1725       GValue *tmp;
1726
1727       tmp = g_new0 (GValue, 1);
1728       g_value_init (tmp, GST_TYPE_FRACTION);
1729
1730       if (!g_value_transform (value, tmp)) {
1731         GST_WARNING_OBJECT (ximagesink,
1732             "Could not transform string to aspect ratio");
1733         g_free (tmp);
1734       } else {
1735         GST_DEBUG_OBJECT (ximagesink, "set PAR to %d/%d",
1736             gst_value_get_fraction_numerator (tmp),
1737             gst_value_get_fraction_denominator (tmp));
1738         g_free (ximagesink->par);
1739         ximagesink->par = tmp;
1740       }
1741     }
1742       break;
1743     case PROP_HANDLE_EVENTS:
1744       gst_ximagesink_set_event_handling (GST_VIDEO_OVERLAY (ximagesink),
1745           g_value_get_boolean (value));
1746       gst_ximagesink_manage_event_thread (ximagesink);
1747       break;
1748     case PROP_HANDLE_EXPOSE:
1749       ximagesink->handle_expose = g_value_get_boolean (value);
1750       gst_ximagesink_manage_event_thread (ximagesink);
1751       break;
1752     default:
1753       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1754       break;
1755   }
1756 }
1757
1758 static void
1759 gst_ximagesink_get_property (GObject * object, guint prop_id,
1760     GValue * value, GParamSpec * pspec)
1761 {
1762   GstXImageSink *ximagesink;
1763
1764   g_return_if_fail (GST_IS_XIMAGESINK (object));
1765
1766   ximagesink = GST_XIMAGESINK (object);
1767
1768   switch (prop_id) {
1769     case PROP_DISPLAY:
1770       g_value_set_string (value, ximagesink->display_name);
1771       break;
1772     case PROP_SYNCHRONOUS:
1773       g_value_set_boolean (value, ximagesink->synchronous);
1774       break;
1775     case PROP_FORCE_ASPECT_RATIO:
1776       g_value_set_boolean (value, ximagesink->keep_aspect);
1777       break;
1778     case PROP_PIXEL_ASPECT_RATIO:
1779       if (ximagesink->par)
1780         g_value_transform (ximagesink->par, value);
1781       break;
1782     case PROP_HANDLE_EVENTS:
1783       g_value_set_boolean (value, ximagesink->handle_events);
1784       break;
1785     case PROP_HANDLE_EXPOSE:
1786       g_value_set_boolean (value, ximagesink->handle_expose);
1787       break;
1788     case PROP_WINDOW_WIDTH:
1789       if (ximagesink->xwindow)
1790         g_value_set_uint64 (value, ximagesink->xwindow->width);
1791       else
1792         g_value_set_uint64 (value, 0);
1793       break;
1794     case PROP_WINDOW_HEIGHT:
1795       if (ximagesink->xwindow)
1796         g_value_set_uint64 (value, ximagesink->xwindow->height);
1797       else
1798         g_value_set_uint64 (value, 0);
1799       break;
1800     default:
1801       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1802       break;
1803   }
1804 }
1805
1806 static void
1807 gst_ximagesink_reset (GstXImageSink * ximagesink)
1808 {
1809   GThread *thread;
1810
1811   GST_OBJECT_LOCK (ximagesink);
1812   ximagesink->running = FALSE;
1813   /* grab thread and mark it as NULL */
1814   thread = ximagesink->event_thread;
1815   ximagesink->event_thread = NULL;
1816   GST_OBJECT_UNLOCK (ximagesink);
1817
1818   /* Wait for our event thread to finish before we clean up our stuff. */
1819   if (thread)
1820     g_thread_join (thread);
1821
1822   if (ximagesink->cur_image) {
1823     gst_buffer_unref (ximagesink->cur_image);
1824     ximagesink->cur_image = NULL;
1825   }
1826
1827   g_mutex_lock (ximagesink->flow_lock);
1828
1829   if (ximagesink->pool) {
1830     gst_object_unref (ximagesink->pool);
1831     ximagesink->pool = NULL;
1832   }
1833
1834   if (ximagesink->xwindow) {
1835     gst_ximagesink_xwindow_clear (ximagesink, ximagesink->xwindow);
1836     gst_ximagesink_xwindow_destroy (ximagesink, ximagesink->xwindow);
1837     ximagesink->xwindow = NULL;
1838   }
1839   g_mutex_unlock (ximagesink->flow_lock);
1840
1841   gst_ximagesink_xcontext_clear (ximagesink);
1842 }
1843
1844 static void
1845 gst_ximagesink_finalize (GObject * object)
1846 {
1847   GstXImageSink *ximagesink;
1848
1849   ximagesink = GST_XIMAGESINK (object);
1850
1851   gst_ximagesink_reset (ximagesink);
1852
1853   if (ximagesink->display_name) {
1854     g_free (ximagesink->display_name);
1855     ximagesink->display_name = NULL;
1856   }
1857   if (ximagesink->par) {
1858     g_free (ximagesink->par);
1859     ximagesink->par = NULL;
1860   }
1861   if (ximagesink->x_lock) {
1862     g_mutex_free (ximagesink->x_lock);
1863     ximagesink->x_lock = NULL;
1864   }
1865   if (ximagesink->flow_lock) {
1866     g_mutex_free (ximagesink->flow_lock);
1867     ximagesink->flow_lock = NULL;
1868   }
1869
1870   g_free (ximagesink->media_title);
1871
1872   G_OBJECT_CLASS (parent_class)->finalize (object);
1873 }
1874
1875 static void
1876 gst_ximagesink_init (GstXImageSink * ximagesink)
1877 {
1878   ximagesink->display_name = NULL;
1879   ximagesink->xcontext = NULL;
1880   ximagesink->xwindow = NULL;
1881   ximagesink->cur_image = NULL;
1882
1883   ximagesink->event_thread = NULL;
1884   ximagesink->running = FALSE;
1885
1886   ximagesink->fps_n = 0;
1887   ximagesink->fps_d = 1;
1888
1889   ximagesink->x_lock = g_mutex_new ();
1890   ximagesink->flow_lock = g_mutex_new ();
1891
1892   ximagesink->par = NULL;
1893
1894   ximagesink->pool = NULL;
1895
1896   ximagesink->synchronous = FALSE;
1897   ximagesink->keep_aspect = TRUE;
1898   ximagesink->handle_events = TRUE;
1899   ximagesink->handle_expose = TRUE;
1900 }
1901
1902 static void
1903 gst_ximagesink_class_init (GstXImageSinkClass * klass)
1904 {
1905   GObjectClass *gobject_class;
1906   GstElementClass *gstelement_class;
1907   GstBaseSinkClass *gstbasesink_class;
1908   GstVideoSinkClass *videosink_class;
1909
1910   gobject_class = (GObjectClass *) klass;
1911   gstelement_class = (GstElementClass *) klass;
1912   gstbasesink_class = (GstBaseSinkClass *) klass;
1913   videosink_class = (GstVideoSinkClass *) klass;
1914
1915   gobject_class->finalize = gst_ximagesink_finalize;
1916   gobject_class->set_property = gst_ximagesink_set_property;
1917   gobject_class->get_property = gst_ximagesink_get_property;
1918
1919   g_object_class_install_property (gobject_class, PROP_DISPLAY,
1920       g_param_spec_string ("display", "Display", "X Display name",
1921           NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1922   g_object_class_install_property (gobject_class, PROP_SYNCHRONOUS,
1923       g_param_spec_boolean ("synchronous", "Synchronous",
1924           "When enabled, runs the X display in synchronous mode. "
1925           "(unrelated to A/V sync, used only for debugging)", FALSE,
1926           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1927   g_object_class_install_property (gobject_class, PROP_FORCE_ASPECT_RATIO,
1928       g_param_spec_boolean ("force-aspect-ratio", "Force aspect ratio",
1929           "When enabled, reverse caps negotiation (scaling) will respect "
1930           "original aspect ratio", TRUE,
1931           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1932   g_object_class_install_property (gobject_class, PROP_PIXEL_ASPECT_RATIO,
1933       g_param_spec_string ("pixel-aspect-ratio", "Pixel Aspect Ratio",
1934           "The pixel aspect ratio of the device", "1/1",
1935           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1936   g_object_class_install_property (gobject_class, PROP_HANDLE_EVENTS,
1937       g_param_spec_boolean ("handle-events", "Handle XEvents",
1938           "When enabled, XEvents will be selected and handled", TRUE,
1939           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1940   g_object_class_install_property (gobject_class, PROP_HANDLE_EXPOSE,
1941       g_param_spec_boolean ("handle-expose", "Handle expose",
1942           "When enabled, "
1943           "the current frame will always be drawn in response to X Expose "
1944           "events", TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1945
1946   /**
1947    * GstXImageSink:window-width
1948    *
1949    * Actual width of the video window.
1950    *
1951    * Since: 0.10.32
1952    */
1953   g_object_class_install_property (gobject_class, PROP_WINDOW_WIDTH,
1954       g_param_spec_uint64 ("window-width", "window-width",
1955           "Width of the window", 0, G_MAXUINT64, 0,
1956           G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
1957
1958   /**
1959    * GstXImageSink:window-height
1960    *
1961    * Actual height of the video window.
1962    *
1963    * Since: 0.10.32
1964    */
1965   g_object_class_install_property (gobject_class, PROP_WINDOW_HEIGHT,
1966       g_param_spec_uint64 ("window-height", "window-height",
1967           "Height of the window", 0, G_MAXUINT64, 0,
1968           G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
1969
1970   gst_element_class_set_static_metadata (gstelement_class,
1971       "Video sink", "Sink/Video",
1972       "A standard X based videosink", "Julien Moutte <julien@moutte.net>");
1973
1974   gst_element_class_add_pad_template (gstelement_class,
1975       gst_static_pad_template_get (&gst_ximagesink_sink_template_factory));
1976
1977   gstelement_class->change_state = gst_ximagesink_change_state;
1978
1979   gstbasesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_ximagesink_getcaps);
1980   gstbasesink_class->set_caps = GST_DEBUG_FUNCPTR (gst_ximagesink_setcaps);
1981   gstbasesink_class->get_times = GST_DEBUG_FUNCPTR (gst_ximagesink_get_times);
1982   gstbasesink_class->propose_allocation =
1983       GST_DEBUG_FUNCPTR (gst_ximagesink_propose_allocation);
1984   gstbasesink_class->event = GST_DEBUG_FUNCPTR (gst_ximagesink_event);
1985
1986   videosink_class->show_frame = GST_DEBUG_FUNCPTR (gst_ximagesink_show_frame);
1987 }