ximagesink: add support for 32-bit RGB with alpha mask
[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   guint32 alpha_mask;
836
837   g_return_val_if_fail (GST_IS_XIMAGESINK (ximagesink), NULL);
838
839   xcontext = g_new0 (GstXContext, 1);
840
841   g_mutex_lock (&ximagesink->x_lock);
842
843   xcontext->disp = XOpenDisplay (ximagesink->display_name);
844
845   if (!xcontext->disp) {
846     g_mutex_unlock (&ximagesink->x_lock);
847     g_free (xcontext);
848     GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
849         ("Could not initialise X output"), ("Could not open display"));
850     return NULL;
851   }
852
853   xcontext->screen = DefaultScreenOfDisplay (xcontext->disp);
854   xcontext->screen_num = DefaultScreen (xcontext->disp);
855   xcontext->visual = DefaultVisual (xcontext->disp, xcontext->screen_num);
856   xcontext->root = DefaultRootWindow (xcontext->disp);
857   xcontext->white = XWhitePixel (xcontext->disp, xcontext->screen_num);
858   xcontext->black = XBlackPixel (xcontext->disp, xcontext->screen_num);
859   xcontext->depth = DefaultDepthOfScreen (xcontext->screen);
860
861   xcontext->width = DisplayWidth (xcontext->disp, xcontext->screen_num);
862   xcontext->height = DisplayHeight (xcontext->disp, xcontext->screen_num);
863   xcontext->widthmm = DisplayWidthMM (xcontext->disp, xcontext->screen_num);
864   xcontext->heightmm = DisplayHeightMM (xcontext->disp, xcontext->screen_num);
865
866   GST_DEBUG_OBJECT (ximagesink, "X reports %dx%d pixels and %d mm x %d mm",
867       xcontext->width, xcontext->height, xcontext->widthmm, xcontext->heightmm);
868
869   gst_ximagesink_calculate_pixel_aspect_ratio (xcontext);
870
871   /* We get supported pixmap formats at supported depth */
872   px_formats = XListPixmapFormats (xcontext->disp, &nb_formats);
873
874   if (!px_formats) {
875     XCloseDisplay (xcontext->disp);
876     g_mutex_unlock (&ximagesink->x_lock);
877     g_free (xcontext->par);
878     g_free (xcontext);
879     GST_ELEMENT_ERROR (ximagesink, RESOURCE, SETTINGS,
880         ("Could not get supported pixmap formats"), (NULL));
881     return NULL;
882   }
883
884   /* We get bpp value corresponding to our running depth */
885   for (i = 0; i < nb_formats; i++) {
886     if (px_formats[i].depth == xcontext->depth)
887       xcontext->bpp = px_formats[i].bits_per_pixel;
888   }
889
890   XFree (px_formats);
891
892   endianness = (ImageByteOrder (xcontext->disp) ==
893       LSBFirst) ? G_LITTLE_ENDIAN : G_BIG_ENDIAN;
894
895   /* Search for XShm extension support */
896 #ifdef HAVE_XSHM
897   if (XShmQueryExtension (xcontext->disp) &&
898       gst_ximagesink_check_xshm_calls (ximagesink, xcontext)) {
899     xcontext->use_xshm = TRUE;
900     GST_DEBUG ("ximagesink is using XShm extension");
901   } else
902 #endif /* HAVE_XSHM */
903   {
904     xcontext->use_xshm = FALSE;
905     GST_DEBUG ("ximagesink is not using XShm extension");
906   }
907
908   /* extrapolate alpha mask */
909   alpha_mask = ~(xcontext->visual->red_mask
910       | xcontext->visual->green_mask | xcontext->visual->blue_mask);
911   alpha_mask &= 0xffffffff;
912
913   vformat =
914       gst_video_format_from_masks (xcontext->depth, xcontext->bpp, endianness,
915       xcontext->visual->red_mask, xcontext->visual->green_mask,
916       xcontext->visual->blue_mask, alpha_mask);
917
918   if (vformat == GST_VIDEO_FORMAT_UNKNOWN)
919     goto unknown_format;
920
921   /* update object's par with calculated one if not set yet */
922   if (!ximagesink->par) {
923     ximagesink->par = g_new0 (GValue, 1);
924     gst_value_init_and_copy (ximagesink->par, xcontext->par);
925     GST_DEBUG_OBJECT (ximagesink, "set calculated PAR on object's PAR");
926   }
927   xcontext->caps = gst_caps_new_simple ("video/x-raw",
928       "format", G_TYPE_STRING, gst_video_format_to_string (vformat),
929       "width", GST_TYPE_INT_RANGE, 1, G_MAXINT,
930       "height", GST_TYPE_INT_RANGE, 1, G_MAXINT,
931       "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1, NULL);
932   if (ximagesink->par) {
933     int nom, den;
934
935     nom = gst_value_get_fraction_numerator (ximagesink->par);
936     den = gst_value_get_fraction_denominator (ximagesink->par);
937     gst_caps_set_simple (xcontext->caps, "pixel-aspect-ratio",
938         GST_TYPE_FRACTION, nom, den, NULL);
939   }
940
941   g_mutex_unlock (&ximagesink->x_lock);
942
943   return xcontext;
944
945   /* ERRORS */
946 unknown_format:
947   {
948     GST_ERROR_OBJECT (ximagesink, "unknown format");
949     return NULL;
950   }
951 }
952
953 /* This function cleans the X context. Closing the Display and unrefing the
954    caps for supported formats. */
955 static void
956 gst_ximagesink_xcontext_clear (GstXImageSink * ximagesink)
957 {
958   GstXContext *xcontext;
959
960   g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
961
962   GST_OBJECT_LOCK (ximagesink);
963   if (ximagesink->xcontext == NULL) {
964     GST_OBJECT_UNLOCK (ximagesink);
965     return;
966   }
967
968   /* Take the xcontext reference and NULL it while we
969    * clean it up, so that any buffer-alloced buffers 
970    * arriving after this will be freed correctly */
971   xcontext = ximagesink->xcontext;
972   ximagesink->xcontext = NULL;
973
974   GST_OBJECT_UNLOCK (ximagesink);
975
976   gst_caps_unref (xcontext->caps);
977   g_free (xcontext->par);
978   g_free (ximagesink->par);
979   ximagesink->par = NULL;
980
981   if (xcontext->last_caps)
982     gst_caps_replace (&xcontext->last_caps, NULL);
983
984   g_mutex_lock (&ximagesink->x_lock);
985
986   XCloseDisplay (xcontext->disp);
987
988   g_mutex_unlock (&ximagesink->x_lock);
989
990   g_free (xcontext);
991 }
992
993 /* Element stuff */
994
995 static GstCaps *
996 gst_ximagesink_getcaps (GstBaseSink * bsink, GstCaps * filter)
997 {
998   GstXImageSink *ximagesink;
999   GstCaps *caps;
1000   int i;
1001
1002   ximagesink = GST_XIMAGESINK (bsink);
1003
1004   g_mutex_lock (&ximagesink->x_lock);
1005   if (ximagesink->xcontext) {
1006     GstCaps *caps;
1007
1008     caps = gst_caps_ref (ximagesink->xcontext->caps);
1009
1010     if (filter) {
1011       GstCaps *intersection;
1012
1013       intersection =
1014           gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST);
1015       gst_caps_unref (caps);
1016       caps = intersection;
1017     }
1018
1019     if (gst_caps_is_empty (caps)) {
1020       g_mutex_unlock (&ximagesink->x_lock);
1021       return caps;
1022     }
1023
1024     if (ximagesink->xwindow && ximagesink->xwindow->width) {
1025       GstStructure *s0, *s1;
1026
1027       caps = gst_caps_make_writable (caps);
1028
1029       /* There can only be a single structure because the xcontext
1030        * caps only have a single structure */
1031       s0 = gst_caps_get_structure (caps, 0);
1032       s1 = gst_structure_copy (gst_caps_get_structure (caps, 0));
1033
1034       gst_structure_set (s0, "width", G_TYPE_INT, ximagesink->xwindow->width,
1035           "height", G_TYPE_INT, ximagesink->xwindow->height, NULL);
1036       gst_caps_append_structure (caps, s1);
1037
1038       /* This will not change the order but will remove the
1039        * fixed width/height caps again if not possible
1040        * upstream */
1041       if (filter) {
1042         GstCaps *intersection;
1043
1044         intersection =
1045             gst_caps_intersect_full (caps, filter, GST_CAPS_INTERSECT_FIRST);
1046         gst_caps_unref (caps);
1047         caps = intersection;
1048       }
1049     }
1050
1051     g_mutex_unlock (&ximagesink->x_lock);
1052     return caps;
1053   }
1054   g_mutex_unlock (&ximagesink->x_lock);
1055
1056   /* get a template copy and add the pixel aspect ratio */
1057   caps = gst_pad_get_pad_template_caps (GST_BASE_SINK (ximagesink)->sinkpad);
1058   if (ximagesink->par) {
1059     caps = gst_caps_make_writable (caps);
1060     for (i = 0; i < gst_caps_get_size (caps); ++i) {
1061       GstStructure *structure = gst_caps_get_structure (caps, i);
1062       int nom, den;
1063
1064       nom = gst_value_get_fraction_numerator (ximagesink->par);
1065       den = gst_value_get_fraction_denominator (ximagesink->par);
1066       gst_structure_set (structure, "pixel-aspect-ratio",
1067           GST_TYPE_FRACTION, nom, den, NULL);
1068     }
1069   }
1070
1071   if (filter) {
1072     GstCaps *intersection;
1073
1074     intersection =
1075         gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST);
1076     gst_caps_unref (caps);
1077     caps = intersection;
1078   }
1079
1080   return caps;
1081 }
1082
1083 static gboolean
1084 gst_ximagesink_setcaps (GstBaseSink * bsink, GstCaps * caps)
1085 {
1086   GstXImageSink *ximagesink;
1087   GstStructure *structure;
1088   GstVideoInfo info;
1089   GstBufferPool *newpool, *oldpool;
1090   const GValue *par;
1091   gint size;
1092   static GstAllocationParams params = { 0, 15, 0, 0, };
1093
1094   ximagesink = GST_XIMAGESINK (bsink);
1095
1096   if (!ximagesink->xcontext)
1097     return FALSE;
1098
1099   GST_DEBUG_OBJECT (ximagesink,
1100       "sinkconnect possible caps %" GST_PTR_FORMAT " with given caps %"
1101       GST_PTR_FORMAT, ximagesink->xcontext->caps, caps);
1102
1103   /* We intersect those caps with our template to make sure they are correct */
1104   if (!gst_caps_can_intersect (ximagesink->xcontext->caps, caps))
1105     goto incompatible_caps;
1106
1107   if (!gst_video_info_from_caps (&info, caps))
1108     goto invalid_format;
1109
1110   size = info.size;
1111
1112   structure = gst_caps_get_structure (caps, 0);
1113   /* if the caps contain pixel-aspect-ratio, they have to match ours,
1114    * otherwise linking should fail */
1115   par = gst_structure_get_value (structure, "pixel-aspect-ratio");
1116   if (par) {
1117     if (ximagesink->par) {
1118       if (gst_value_compare (par, ximagesink->par) != GST_VALUE_EQUAL) {
1119         goto wrong_aspect;
1120       }
1121     } else if (ximagesink->xcontext->par) {
1122       if (gst_value_compare (par, ximagesink->xcontext->par) != GST_VALUE_EQUAL) {
1123         goto wrong_aspect;
1124       }
1125     }
1126   }
1127
1128   GST_VIDEO_SINK_WIDTH (ximagesink) = info.width;
1129   GST_VIDEO_SINK_HEIGHT (ximagesink) = info.height;
1130   ximagesink->fps_n = info.fps_n;
1131   ximagesink->fps_d = info.fps_d;
1132
1133   /* Notify application to set xwindow id now */
1134   g_mutex_lock (&ximagesink->flow_lock);
1135   if (!ximagesink->xwindow) {
1136     g_mutex_unlock (&ximagesink->flow_lock);
1137     gst_video_overlay_prepare_window_handle (GST_VIDEO_OVERLAY (ximagesink));
1138   } else {
1139     g_mutex_unlock (&ximagesink->flow_lock);
1140   }
1141
1142   /* Creating our window and our image */
1143   if (GST_VIDEO_SINK_WIDTH (ximagesink) <= 0 ||
1144       GST_VIDEO_SINK_HEIGHT (ximagesink) <= 0)
1145     goto invalid_size;
1146
1147   g_mutex_lock (&ximagesink->flow_lock);
1148   if (!ximagesink->xwindow) {
1149     ximagesink->xwindow = gst_ximagesink_xwindow_new (ximagesink,
1150         GST_VIDEO_SINK_WIDTH (ximagesink), GST_VIDEO_SINK_HEIGHT (ximagesink));
1151   }
1152
1153   ximagesink->info = info;
1154
1155   /* Remember to draw borders for next frame */
1156   ximagesink->draw_border = TRUE;
1157
1158   /* create a new pool for the new configuration */
1159   newpool = gst_ximage_buffer_pool_new (ximagesink);
1160
1161   structure = gst_buffer_pool_get_config (newpool);
1162   gst_buffer_pool_config_set_params (structure, caps, size, 2, 0);
1163   gst_buffer_pool_config_set_allocator (structure, NULL, &params);
1164   if (!gst_buffer_pool_set_config (newpool, structure))
1165     goto config_failed;
1166
1167   oldpool = ximagesink->pool;
1168   /* we don't activate the pool yet, this will be done by downstream after it
1169    * has configured the pool. If downstream does not want our pool we will
1170    * activate it when we render into it */
1171   ximagesink->pool = newpool;
1172   g_mutex_unlock (&ximagesink->flow_lock);
1173
1174   /* unref the old sink */
1175   if (oldpool) {
1176     /* we don't deactivate, some elements might still be using it, it will be
1177      * deactivated when the last ref is gone */
1178     gst_object_unref (oldpool);
1179   }
1180
1181   return TRUE;
1182
1183   /* ERRORS */
1184 incompatible_caps:
1185   {
1186     GST_ERROR_OBJECT (ximagesink, "caps incompatible");
1187     return FALSE;
1188   }
1189 invalid_format:
1190   {
1191     GST_ERROR_OBJECT (ximagesink, "caps invalid");
1192     return FALSE;
1193   }
1194 wrong_aspect:
1195   {
1196     GST_INFO_OBJECT (ximagesink, "pixel aspect ratio does not match");
1197     return FALSE;
1198   }
1199 invalid_size:
1200   {
1201     GST_ELEMENT_ERROR (ximagesink, CORE, NEGOTIATION, (NULL),
1202         ("Invalid image size."));
1203     return FALSE;
1204   }
1205 config_failed:
1206   {
1207     GST_ERROR_OBJECT (ximagesink, "failed to set config.");
1208     g_mutex_unlock (&ximagesink->flow_lock);
1209     return FALSE;
1210   }
1211 }
1212
1213 static GstStateChangeReturn
1214 gst_ximagesink_change_state (GstElement * element, GstStateChange transition)
1215 {
1216   GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
1217   GstXImageSink *ximagesink;
1218   GstXContext *xcontext = NULL;
1219
1220   ximagesink = GST_XIMAGESINK (element);
1221
1222   switch (transition) {
1223     case GST_STATE_CHANGE_NULL_TO_READY:
1224       /* Initializing the XContext */
1225       if (ximagesink->xcontext == NULL) {
1226         xcontext = gst_ximagesink_xcontext_get (ximagesink);
1227         if (xcontext == NULL) {
1228           ret = GST_STATE_CHANGE_FAILURE;
1229           goto beach;
1230         }
1231         GST_OBJECT_LOCK (ximagesink);
1232         if (xcontext)
1233           ximagesink->xcontext = xcontext;
1234         GST_OBJECT_UNLOCK (ximagesink);
1235       }
1236
1237       /* call XSynchronize with the current value of synchronous */
1238       GST_DEBUG_OBJECT (ximagesink, "XSynchronize called with %s",
1239           ximagesink->synchronous ? "TRUE" : "FALSE");
1240       g_mutex_lock (&ximagesink->x_lock);
1241       XSynchronize (ximagesink->xcontext->disp, ximagesink->synchronous);
1242       g_mutex_unlock (&ximagesink->x_lock);
1243       gst_ximagesink_manage_event_thread (ximagesink);
1244       break;
1245     case GST_STATE_CHANGE_READY_TO_PAUSED:
1246       g_mutex_lock (&ximagesink->flow_lock);
1247       if (ximagesink->xwindow)
1248         gst_ximagesink_xwindow_clear (ximagesink, ximagesink->xwindow);
1249       g_mutex_unlock (&ximagesink->flow_lock);
1250       break;
1251     case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
1252       break;
1253     default:
1254       break;
1255   }
1256
1257   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
1258
1259   switch (transition) {
1260     case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
1261       break;
1262     case GST_STATE_CHANGE_PAUSED_TO_READY:
1263       ximagesink->fps_n = 0;
1264       ximagesink->fps_d = 1;
1265       GST_VIDEO_SINK_WIDTH (ximagesink) = 0;
1266       GST_VIDEO_SINK_HEIGHT (ximagesink) = 0;
1267       g_mutex_lock (&ximagesink->flow_lock);
1268       if (ximagesink->pool)
1269         gst_buffer_pool_set_active (ximagesink->pool, FALSE);
1270       g_mutex_unlock (&ximagesink->flow_lock);
1271       break;
1272     case GST_STATE_CHANGE_READY_TO_NULL:
1273       gst_ximagesink_reset (ximagesink);
1274       break;
1275     default:
1276       break;
1277   }
1278
1279 beach:
1280   return ret;
1281 }
1282
1283 static void
1284 gst_ximagesink_get_times (GstBaseSink * bsink, GstBuffer * buf,
1285     GstClockTime * start, GstClockTime * end)
1286 {
1287   GstXImageSink *ximagesink;
1288
1289   ximagesink = GST_XIMAGESINK (bsink);
1290
1291   if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
1292     *start = GST_BUFFER_TIMESTAMP (buf);
1293     if (GST_BUFFER_DURATION_IS_VALID (buf)) {
1294       *end = *start + GST_BUFFER_DURATION (buf);
1295     } else {
1296       if (ximagesink->fps_n > 0) {
1297         *end = *start +
1298             gst_util_uint64_scale_int (GST_SECOND, ximagesink->fps_d,
1299             ximagesink->fps_n);
1300       }
1301     }
1302   }
1303 }
1304
1305 static GstFlowReturn
1306 gst_ximagesink_show_frame (GstVideoSink * vsink, GstBuffer * buf)
1307 {
1308   GstFlowReturn res;
1309   GstXImageSink *ximagesink;
1310   GstXImageMemory *mem;
1311   GstBuffer *to_put = NULL;
1312
1313   ximagesink = GST_XIMAGESINK (vsink);
1314
1315   if (gst_buffer_n_memory (buf) == 1
1316       && (mem = (GstXImageMemory *) gst_buffer_peek_memory (buf, 0))
1317       && g_strcmp0 (mem->parent.allocator->mem_type, "ximage") == 0
1318       && mem->sink == ximagesink) {
1319     /* If this buffer has been allocated using our buffer management we simply
1320        put the ximage which is in the PRIVATE pointer */
1321     GST_LOG_OBJECT (ximagesink, "buffer from our pool, writing directly");
1322     to_put = buf;
1323     res = GST_FLOW_OK;
1324   } else {
1325     GstVideoFrame src, dest;
1326     GstBufferPoolAcquireParams params = { 0, };
1327
1328     /* Else we have to copy the data into our private image, */
1329     /* if we have one... */
1330     GST_LOG_OBJECT (ximagesink, "buffer not from our pool, copying");
1331
1332     /* we should have a pool, configured in setcaps */
1333     if (ximagesink->pool == NULL)
1334       goto no_pool;
1335
1336     if (!gst_buffer_pool_set_active (ximagesink->pool, TRUE))
1337       goto activate_failed;
1338
1339     /* take a buffer from our pool, if there is no buffer in the pool something
1340      * is seriously wrong, waiting for the pool here might deadlock when we try
1341      * to go to PAUSED because we never flush the pool. */
1342     params.flags = GST_BUFFER_POOL_ACQUIRE_FLAG_DONTWAIT;
1343     res = gst_buffer_pool_acquire_buffer (ximagesink->pool, &to_put, &params);
1344     if (res != GST_FLOW_OK)
1345       goto no_buffer;
1346
1347     GST_CAT_LOG_OBJECT (GST_CAT_PERFORMANCE, ximagesink,
1348         "slow copy into bufferpool buffer %p", to_put);
1349
1350     if (!gst_video_frame_map (&src, &ximagesink->info, buf, GST_MAP_READ))
1351       goto invalid_buffer;
1352
1353     if (!gst_video_frame_map (&dest, &ximagesink->info, to_put, GST_MAP_WRITE)) {
1354       gst_video_frame_unmap (&src);
1355       goto invalid_buffer;
1356     }
1357
1358     gst_video_frame_copy (&dest, &src);
1359
1360     gst_video_frame_unmap (&dest);
1361     gst_video_frame_unmap (&src);
1362   }
1363
1364   if (!gst_ximagesink_ximage_put (ximagesink, to_put))
1365     goto no_window;
1366
1367 done:
1368   if (to_put != buf)
1369     gst_buffer_unref (to_put);
1370
1371   return res;
1372
1373   /* ERRORS */
1374 no_pool:
1375   {
1376     GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
1377         ("Internal error: can't allocate images"),
1378         ("We don't have a bufferpool negotiated"));
1379     return GST_FLOW_ERROR;
1380   }
1381 no_buffer:
1382   {
1383     /* No image available. That's very bad ! */
1384     GST_WARNING_OBJECT (ximagesink, "could not create image");
1385     return GST_FLOW_OK;
1386   }
1387 invalid_buffer:
1388   {
1389     /* No Window available to put our image into */
1390     GST_WARNING_OBJECT (ximagesink, "could not map image");
1391     res = GST_FLOW_OK;
1392     goto done;
1393   }
1394 no_window:
1395   {
1396     /* No Window available to put our image into */
1397     GST_WARNING_OBJECT (ximagesink, "could not output image - no window");
1398     res = GST_FLOW_ERROR;
1399     goto done;
1400   }
1401 activate_failed:
1402   {
1403     GST_ERROR_OBJECT (ximagesink, "failed to activate bufferpool.");
1404     res = GST_FLOW_ERROR;
1405     goto done;
1406   }
1407 }
1408
1409 static gboolean
1410 gst_ximagesink_event (GstBaseSink * sink, GstEvent * event)
1411 {
1412   GstXImageSink *ximagesink = GST_XIMAGESINK (sink);
1413
1414   switch (GST_EVENT_TYPE (event)) {
1415     case GST_EVENT_TAG:{
1416       GstTagList *l;
1417       gchar *title = NULL;
1418
1419       gst_event_parse_tag (event, &l);
1420       gst_tag_list_get_string (l, GST_TAG_TITLE, &title);
1421
1422       if (title) {
1423         GST_DEBUG_OBJECT (ximagesink, "got tags, title='%s'", title);
1424         gst_ximagesink_xwindow_set_title (ximagesink, ximagesink->xwindow,
1425             title);
1426
1427         g_free (title);
1428       }
1429       break;
1430     }
1431     default:
1432       break;
1433   }
1434   return GST_BASE_SINK_CLASS (parent_class)->event (sink, event);
1435 }
1436
1437 static gboolean
1438 gst_ximagesink_propose_allocation (GstBaseSink * bsink, GstQuery * query)
1439 {
1440   GstXImageSink *ximagesink = GST_XIMAGESINK (bsink);
1441   GstBufferPool *pool;
1442   GstStructure *config;
1443   GstCaps *caps;
1444   guint size;
1445   gboolean need_pool;
1446
1447   gst_query_parse_allocation (query, &caps, &need_pool);
1448
1449   if (caps == NULL)
1450     goto no_caps;
1451
1452   g_mutex_lock (&ximagesink->flow_lock);
1453   if ((pool = ximagesink->pool))
1454     gst_object_ref (pool);
1455   g_mutex_unlock (&ximagesink->flow_lock);
1456
1457   if (pool != NULL) {
1458     GstCaps *pcaps;
1459
1460     /* we had a pool, check caps */
1461     config = gst_buffer_pool_get_config (pool);
1462     gst_buffer_pool_config_get_params (config, &pcaps, &size, NULL, NULL);
1463
1464     GST_DEBUG_OBJECT (ximagesink,
1465         "we had a pool with caps %" GST_PTR_FORMAT, pcaps);
1466     if (!gst_caps_is_equal (caps, pcaps)) {
1467       /* different caps, we can't use this pool */
1468       GST_DEBUG_OBJECT (ximagesink, "pool has different caps");
1469       gst_object_unref (pool);
1470       pool = NULL;
1471     }
1472     gst_structure_free (config);
1473   }
1474   if (pool == NULL && need_pool) {
1475     GstVideoInfo info;
1476
1477     if (!gst_video_info_from_caps (&info, caps))
1478       goto invalid_caps;
1479
1480     GST_DEBUG_OBJECT (ximagesink, "create new pool");
1481     pool = gst_ximage_buffer_pool_new (ximagesink);
1482
1483     /* the normal size of a frame */
1484     size = info.size;
1485
1486     config = gst_buffer_pool_get_config (pool);
1487     gst_buffer_pool_config_set_params (config, caps, size, 0, 0);
1488     if (!gst_buffer_pool_set_config (pool, config))
1489       goto config_failed;
1490   }
1491   if (pool) {
1492     /* we need at least 2 buffer because we hold on to the last one */
1493     gst_query_add_allocation_pool (query, pool, size, 2, 0);
1494     gst_object_unref (pool);
1495   }
1496
1497   /* we also support various metadata */
1498   gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL);
1499   gst_query_add_allocation_meta (query, GST_VIDEO_CROP_META_API_TYPE, NULL);
1500
1501   return TRUE;
1502
1503   /* ERRORS */
1504 no_caps:
1505   {
1506     GST_DEBUG_OBJECT (bsink, "no caps specified");
1507     return FALSE;
1508   }
1509 invalid_caps:
1510   {
1511     GST_DEBUG_OBJECT (bsink, "invalid caps specified");
1512     return FALSE;
1513   }
1514 config_failed:
1515   {
1516     GST_DEBUG_OBJECT (bsink, "failed setting config");
1517     gst_object_unref (pool);
1518     return FALSE;
1519   }
1520 }
1521
1522 /* Interfaces stuff */
1523 static void
1524 gst_ximagesink_navigation_send_event (GstNavigation * navigation,
1525     GstStructure * structure)
1526 {
1527   GstXImageSink *ximagesink = GST_XIMAGESINK (navigation);
1528   GstEvent *event;
1529   gint x_offset, y_offset;
1530   gdouble x, y;
1531   GstPad *pad = NULL;
1532
1533   event = gst_event_new_navigation (structure);
1534
1535   /* We are not converting the pointer coordinates as there's no hardware
1536      scaling done here. The only possible scaling is done by videoscale and
1537      videoscale will have to catch those events and tranform the coordinates
1538      to match the applied scaling. So here we just add the offset if the image
1539      is centered in the window.  */
1540
1541   /* We take the flow_lock while we look at the window */
1542   g_mutex_lock (&ximagesink->flow_lock);
1543
1544   if (!ximagesink->xwindow) {
1545     g_mutex_unlock (&ximagesink->flow_lock);
1546     return;
1547   }
1548
1549   x_offset = ximagesink->xwindow->width - GST_VIDEO_SINK_WIDTH (ximagesink);
1550   y_offset = ximagesink->xwindow->height - GST_VIDEO_SINK_HEIGHT (ximagesink);
1551
1552   g_mutex_unlock (&ximagesink->flow_lock);
1553
1554   if (x_offset > 0 && gst_structure_get_double (structure, "pointer_x", &x)) {
1555     x -= x_offset / 2;
1556     gst_structure_set (structure, "pointer_x", G_TYPE_DOUBLE, x, NULL);
1557   }
1558   if (y_offset > 0 && gst_structure_get_double (structure, "pointer_y", &y)) {
1559     y -= y_offset / 2;
1560     gst_structure_set (structure, "pointer_y", G_TYPE_DOUBLE, y, NULL);
1561   }
1562
1563   pad = gst_pad_get_peer (GST_VIDEO_SINK_PAD (ximagesink));
1564
1565   if (GST_IS_PAD (pad) && GST_IS_EVENT (event)) {
1566     gst_pad_send_event (pad, event);
1567
1568     gst_object_unref (pad);
1569   }
1570 }
1571
1572 static void
1573 gst_ximagesink_navigation_init (GstNavigationInterface * iface)
1574 {
1575   iface->send_event = gst_ximagesink_navigation_send_event;
1576 }
1577
1578 static void
1579 gst_ximagesink_set_window_handle (GstVideoOverlay * overlay, guintptr id)
1580 {
1581   XID xwindow_id = id;
1582   GstXImageSink *ximagesink = GST_XIMAGESINK (overlay);
1583   GstXWindow *xwindow = NULL;
1584   XWindowAttributes attr;
1585
1586   /* We acquire the stream lock while setting this window in the element.
1587      We are basically cleaning tons of stuff replacing the old window, putting
1588      images while we do that would surely crash */
1589   g_mutex_lock (&ximagesink->flow_lock);
1590
1591   /* If we already use that window return */
1592   if (ximagesink->xwindow && (xwindow_id == ximagesink->xwindow->win)) {
1593     g_mutex_unlock (&ximagesink->flow_lock);
1594     return;
1595   }
1596
1597   /* If the element has not initialized the X11 context try to do so */
1598   if (!ximagesink->xcontext &&
1599       !(ximagesink->xcontext = gst_ximagesink_xcontext_get (ximagesink))) {
1600     g_mutex_unlock (&ximagesink->flow_lock);
1601     /* we have thrown a GST_ELEMENT_ERROR now */
1602     return;
1603   }
1604
1605   /* If a window is there already we destroy it */
1606   if (ximagesink->xwindow) {
1607     gst_ximagesink_xwindow_destroy (ximagesink, ximagesink->xwindow);
1608     ximagesink->xwindow = NULL;
1609   }
1610
1611   /* If the xid is 0 we go back to an internal window */
1612   if (xwindow_id == 0) {
1613     /* If no width/height caps nego did not happen window will be created
1614        during caps nego then */
1615     if (GST_VIDEO_SINK_WIDTH (ximagesink) && GST_VIDEO_SINK_HEIGHT (ximagesink)) {
1616       xwindow = gst_ximagesink_xwindow_new (ximagesink,
1617           GST_VIDEO_SINK_WIDTH (ximagesink),
1618           GST_VIDEO_SINK_HEIGHT (ximagesink));
1619     }
1620   } else {
1621     xwindow = g_new0 (GstXWindow, 1);
1622
1623     xwindow->win = xwindow_id;
1624
1625     /* We get window geometry, set the event we want to receive,
1626        and create a GC */
1627     g_mutex_lock (&ximagesink->x_lock);
1628     XGetWindowAttributes (ximagesink->xcontext->disp, xwindow->win, &attr);
1629     xwindow->width = attr.width;
1630     xwindow->height = attr.height;
1631     xwindow->internal = FALSE;
1632     if (ximagesink->handle_events) {
1633       XSelectInput (ximagesink->xcontext->disp, xwindow->win, ExposureMask |
1634           StructureNotifyMask | PointerMotionMask | KeyPressMask |
1635           KeyReleaseMask);
1636     }
1637
1638     xwindow->gc = XCreateGC (ximagesink->xcontext->disp, xwindow->win, 0, NULL);
1639     g_mutex_unlock (&ximagesink->x_lock);
1640   }
1641
1642   if (xwindow)
1643     ximagesink->xwindow = xwindow;
1644
1645   g_mutex_unlock (&ximagesink->flow_lock);
1646 }
1647
1648 static void
1649 gst_ximagesink_expose (GstVideoOverlay * overlay)
1650 {
1651   GstXImageSink *ximagesink = GST_XIMAGESINK (overlay);
1652
1653   gst_ximagesink_xwindow_update_geometry (ximagesink);
1654   gst_ximagesink_ximage_put (ximagesink, NULL);
1655 }
1656
1657 static void
1658 gst_ximagesink_set_event_handling (GstVideoOverlay * overlay,
1659     gboolean handle_events)
1660 {
1661   GstXImageSink *ximagesink = GST_XIMAGESINK (overlay);
1662
1663   ximagesink->handle_events = handle_events;
1664
1665   g_mutex_lock (&ximagesink->flow_lock);
1666
1667   if (G_UNLIKELY (!ximagesink->xwindow)) {
1668     g_mutex_unlock (&ximagesink->flow_lock);
1669     return;
1670   }
1671
1672   g_mutex_lock (&ximagesink->x_lock);
1673
1674   if (handle_events) {
1675     if (ximagesink->xwindow->internal) {
1676       XSelectInput (ximagesink->xcontext->disp, ximagesink->xwindow->win,
1677           ExposureMask | StructureNotifyMask | PointerMotionMask |
1678           KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask);
1679     } else {
1680       XSelectInput (ximagesink->xcontext->disp, ximagesink->xwindow->win,
1681           ExposureMask | StructureNotifyMask | PointerMotionMask |
1682           KeyPressMask | KeyReleaseMask);
1683     }
1684   } else {
1685     XSelectInput (ximagesink->xcontext->disp, ximagesink->xwindow->win, 0);
1686   }
1687
1688   g_mutex_unlock (&ximagesink->x_lock);
1689
1690   g_mutex_unlock (&ximagesink->flow_lock);
1691 }
1692
1693 static void
1694 gst_ximagesink_video_overlay_init (GstVideoOverlayInterface * iface)
1695 {
1696   iface->set_window_handle = gst_ximagesink_set_window_handle;
1697   iface->expose = gst_ximagesink_expose;
1698   iface->handle_events = gst_ximagesink_set_event_handling;
1699 }
1700
1701 /* =========================================== */
1702 /*                                             */
1703 /*              Init & Class init              */
1704 /*                                             */
1705 /* =========================================== */
1706
1707 static void
1708 gst_ximagesink_set_property (GObject * object, guint prop_id,
1709     const GValue * value, GParamSpec * pspec)
1710 {
1711   GstXImageSink *ximagesink;
1712
1713   g_return_if_fail (GST_IS_XIMAGESINK (object));
1714
1715   ximagesink = GST_XIMAGESINK (object);
1716
1717   switch (prop_id) {
1718     case PROP_DISPLAY:
1719       ximagesink->display_name = g_strdup (g_value_get_string (value));
1720       break;
1721     case PROP_SYNCHRONOUS:
1722       ximagesink->synchronous = g_value_get_boolean (value);
1723       if (ximagesink->xcontext) {
1724         GST_DEBUG_OBJECT (ximagesink, "XSynchronize called with %s",
1725             ximagesink->synchronous ? "TRUE" : "FALSE");
1726         g_mutex_lock (&ximagesink->x_lock);
1727         XSynchronize (ximagesink->xcontext->disp, ximagesink->synchronous);
1728         g_mutex_unlock (&ximagesink->x_lock);
1729       }
1730       break;
1731     case PROP_FORCE_ASPECT_RATIO:
1732       ximagesink->keep_aspect = g_value_get_boolean (value);
1733       break;
1734     case PROP_PIXEL_ASPECT_RATIO:
1735     {
1736       GValue *tmp;
1737
1738       tmp = g_new0 (GValue, 1);
1739       g_value_init (tmp, GST_TYPE_FRACTION);
1740
1741       if (!g_value_transform (value, tmp)) {
1742         GST_WARNING_OBJECT (ximagesink,
1743             "Could not transform string to aspect ratio");
1744         g_free (tmp);
1745       } else {
1746         GST_DEBUG_OBJECT (ximagesink, "set PAR to %d/%d",
1747             gst_value_get_fraction_numerator (tmp),
1748             gst_value_get_fraction_denominator (tmp));
1749         g_free (ximagesink->par);
1750         ximagesink->par = tmp;
1751       }
1752     }
1753       break;
1754     case PROP_HANDLE_EVENTS:
1755       gst_ximagesink_set_event_handling (GST_VIDEO_OVERLAY (ximagesink),
1756           g_value_get_boolean (value));
1757       gst_ximagesink_manage_event_thread (ximagesink);
1758       break;
1759     case PROP_HANDLE_EXPOSE:
1760       ximagesink->handle_expose = g_value_get_boolean (value);
1761       gst_ximagesink_manage_event_thread (ximagesink);
1762       break;
1763     default:
1764       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1765       break;
1766   }
1767 }
1768
1769 static void
1770 gst_ximagesink_get_property (GObject * object, guint prop_id,
1771     GValue * value, GParamSpec * pspec)
1772 {
1773   GstXImageSink *ximagesink;
1774
1775   g_return_if_fail (GST_IS_XIMAGESINK (object));
1776
1777   ximagesink = GST_XIMAGESINK (object);
1778
1779   switch (prop_id) {
1780     case PROP_DISPLAY:
1781       g_value_set_string (value, ximagesink->display_name);
1782       break;
1783     case PROP_SYNCHRONOUS:
1784       g_value_set_boolean (value, ximagesink->synchronous);
1785       break;
1786     case PROP_FORCE_ASPECT_RATIO:
1787       g_value_set_boolean (value, ximagesink->keep_aspect);
1788       break;
1789     case PROP_PIXEL_ASPECT_RATIO:
1790       if (ximagesink->par)
1791         g_value_transform (ximagesink->par, value);
1792       break;
1793     case PROP_HANDLE_EVENTS:
1794       g_value_set_boolean (value, ximagesink->handle_events);
1795       break;
1796     case PROP_HANDLE_EXPOSE:
1797       g_value_set_boolean (value, ximagesink->handle_expose);
1798       break;
1799     case PROP_WINDOW_WIDTH:
1800       if (ximagesink->xwindow)
1801         g_value_set_uint64 (value, ximagesink->xwindow->width);
1802       else
1803         g_value_set_uint64 (value, 0);
1804       break;
1805     case PROP_WINDOW_HEIGHT:
1806       if (ximagesink->xwindow)
1807         g_value_set_uint64 (value, ximagesink->xwindow->height);
1808       else
1809         g_value_set_uint64 (value, 0);
1810       break;
1811     default:
1812       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1813       break;
1814   }
1815 }
1816
1817 static void
1818 gst_ximagesink_reset (GstXImageSink * ximagesink)
1819 {
1820   GThread *thread;
1821
1822   GST_OBJECT_LOCK (ximagesink);
1823   ximagesink->running = FALSE;
1824   /* grab thread and mark it as NULL */
1825   thread = ximagesink->event_thread;
1826   ximagesink->event_thread = NULL;
1827   GST_OBJECT_UNLOCK (ximagesink);
1828
1829   /* Wait for our event thread to finish before we clean up our stuff. */
1830   if (thread)
1831     g_thread_join (thread);
1832
1833   if (ximagesink->cur_image) {
1834     gst_buffer_unref (ximagesink->cur_image);
1835     ximagesink->cur_image = NULL;
1836   }
1837
1838   g_mutex_lock (&ximagesink->flow_lock);
1839
1840   if (ximagesink->pool) {
1841     gst_object_unref (ximagesink->pool);
1842     ximagesink->pool = NULL;
1843   }
1844
1845   if (ximagesink->xwindow) {
1846     gst_ximagesink_xwindow_clear (ximagesink, ximagesink->xwindow);
1847     gst_ximagesink_xwindow_destroy (ximagesink, ximagesink->xwindow);
1848     ximagesink->xwindow = NULL;
1849   }
1850   g_mutex_unlock (&ximagesink->flow_lock);
1851
1852   gst_ximagesink_xcontext_clear (ximagesink);
1853 }
1854
1855 static void
1856 gst_ximagesink_finalize (GObject * object)
1857 {
1858   GstXImageSink *ximagesink;
1859
1860   ximagesink = GST_XIMAGESINK (object);
1861
1862   gst_ximagesink_reset (ximagesink);
1863
1864   if (ximagesink->display_name) {
1865     g_free (ximagesink->display_name);
1866     ximagesink->display_name = NULL;
1867   }
1868   if (ximagesink->par) {
1869     g_free (ximagesink->par);
1870     ximagesink->par = NULL;
1871   }
1872   g_mutex_clear (&ximagesink->x_lock);
1873   g_mutex_clear (&ximagesink->flow_lock);
1874
1875   g_free (ximagesink->media_title);
1876
1877   G_OBJECT_CLASS (parent_class)->finalize (object);
1878 }
1879
1880 static void
1881 gst_ximagesink_init (GstXImageSink * ximagesink)
1882 {
1883   ximagesink->display_name = NULL;
1884   ximagesink->xcontext = NULL;
1885   ximagesink->xwindow = NULL;
1886   ximagesink->cur_image = NULL;
1887
1888   ximagesink->event_thread = NULL;
1889   ximagesink->running = FALSE;
1890
1891   ximagesink->fps_n = 0;
1892   ximagesink->fps_d = 1;
1893
1894   g_mutex_init (&ximagesink->x_lock);
1895   g_mutex_init (&ximagesink->flow_lock);
1896
1897   ximagesink->par = NULL;
1898
1899   ximagesink->pool = NULL;
1900
1901   ximagesink->synchronous = FALSE;
1902   ximagesink->keep_aspect = TRUE;
1903   ximagesink->handle_events = TRUE;
1904   ximagesink->handle_expose = TRUE;
1905 }
1906
1907 static void
1908 gst_ximagesink_class_init (GstXImageSinkClass * klass)
1909 {
1910   GObjectClass *gobject_class;
1911   GstElementClass *gstelement_class;
1912   GstBaseSinkClass *gstbasesink_class;
1913   GstVideoSinkClass *videosink_class;
1914
1915   gobject_class = (GObjectClass *) klass;
1916   gstelement_class = (GstElementClass *) klass;
1917   gstbasesink_class = (GstBaseSinkClass *) klass;
1918   videosink_class = (GstVideoSinkClass *) klass;
1919
1920   gobject_class->finalize = gst_ximagesink_finalize;
1921   gobject_class->set_property = gst_ximagesink_set_property;
1922   gobject_class->get_property = gst_ximagesink_get_property;
1923
1924   g_object_class_install_property (gobject_class, PROP_DISPLAY,
1925       g_param_spec_string ("display", "Display", "X Display name",
1926           NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1927   g_object_class_install_property (gobject_class, PROP_SYNCHRONOUS,
1928       g_param_spec_boolean ("synchronous", "Synchronous",
1929           "When enabled, runs the X display in synchronous mode. "
1930           "(unrelated to A/V sync, used only for debugging)", FALSE,
1931           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1932   g_object_class_install_property (gobject_class, PROP_FORCE_ASPECT_RATIO,
1933       g_param_spec_boolean ("force-aspect-ratio", "Force aspect ratio",
1934           "When enabled, reverse caps negotiation (scaling) will respect "
1935           "original aspect ratio", TRUE,
1936           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1937   g_object_class_install_property (gobject_class, PROP_PIXEL_ASPECT_RATIO,
1938       g_param_spec_string ("pixel-aspect-ratio", "Pixel Aspect Ratio",
1939           "The pixel aspect ratio of the device", "1/1",
1940           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1941   g_object_class_install_property (gobject_class, PROP_HANDLE_EVENTS,
1942       g_param_spec_boolean ("handle-events", "Handle XEvents",
1943           "When enabled, XEvents will be selected and handled", TRUE,
1944           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1945   g_object_class_install_property (gobject_class, PROP_HANDLE_EXPOSE,
1946       g_param_spec_boolean ("handle-expose", "Handle expose",
1947           "When enabled, "
1948           "the current frame will always be drawn in response to X Expose "
1949           "events", TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1950
1951   /**
1952    * GstXImageSink:window-width
1953    *
1954    * Actual width of the video window.
1955    *
1956    * Since: 0.10.32
1957    */
1958   g_object_class_install_property (gobject_class, PROP_WINDOW_WIDTH,
1959       g_param_spec_uint64 ("window-width", "window-width",
1960           "Width of the window", 0, G_MAXUINT64, 0,
1961           G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
1962
1963   /**
1964    * GstXImageSink:window-height
1965    *
1966    * Actual height of the video window.
1967    *
1968    * Since: 0.10.32
1969    */
1970   g_object_class_install_property (gobject_class, PROP_WINDOW_HEIGHT,
1971       g_param_spec_uint64 ("window-height", "window-height",
1972           "Height of the window", 0, G_MAXUINT64, 0,
1973           G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
1974
1975   gst_element_class_set_static_metadata (gstelement_class,
1976       "Video sink", "Sink/Video",
1977       "A standard X based videosink", "Julien Moutte <julien@moutte.net>");
1978
1979   gst_element_class_add_pad_template (gstelement_class,
1980       gst_static_pad_template_get (&gst_ximagesink_sink_template_factory));
1981
1982   gstelement_class->change_state = gst_ximagesink_change_state;
1983
1984   gstbasesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_ximagesink_getcaps);
1985   gstbasesink_class->set_caps = GST_DEBUG_FUNCPTR (gst_ximagesink_setcaps);
1986   gstbasesink_class->get_times = GST_DEBUG_FUNCPTR (gst_ximagesink_get_times);
1987   gstbasesink_class->propose_allocation =
1988       GST_DEBUG_FUNCPTR (gst_ximagesink_propose_allocation);
1989   gstbasesink_class->event = GST_DEBUG_FUNCPTR (gst_ximagesink_event);
1990
1991   videosink_class->show_frame = GST_DEBUG_FUNCPTR (gst_ximagesink_show_frame);
1992 }