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