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