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