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