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