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