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