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