take padding into account
[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, 0, 15);
1152   if (!gst_buffer_pool_set_config (newpool, structure))
1153     goto config_failed;
1154
1155   oldpool = ximagesink->pool;
1156   /* we don't activate the pool yet, this will be done by downstream after it
1157    * has configured the pool. If downstream does not want our pool we will
1158    * activate it when we render into it */
1159   ximagesink->pool = newpool;
1160   g_mutex_unlock (ximagesink->flow_lock);
1161
1162   /* unref the old sink */
1163   if (oldpool) {
1164     /* we don't deactivate, some elements might still be using it, it will be
1165      * deactivated when the last ref is gone */
1166     gst_object_unref (oldpool);
1167   }
1168
1169   return TRUE;
1170
1171   /* ERRORS */
1172 incompatible_caps:
1173   {
1174     GST_ERROR_OBJECT (ximagesink, "caps incompatible");
1175     return FALSE;
1176   }
1177 invalid_format:
1178   {
1179     GST_ERROR_OBJECT (ximagesink, "caps invalid");
1180     return FALSE;
1181   }
1182 wrong_aspect:
1183   {
1184     GST_INFO_OBJECT (ximagesink, "pixel aspect ratio does not match");
1185     return FALSE;
1186   }
1187 invalid_size:
1188   {
1189     GST_ELEMENT_ERROR (ximagesink, CORE, NEGOTIATION, (NULL),
1190         ("Invalid image size."));
1191     return FALSE;
1192   }
1193 config_failed:
1194   {
1195     GST_ERROR_OBJECT (ximagesink, "failed to set config.");
1196     g_mutex_unlock (ximagesink->flow_lock);
1197     return FALSE;
1198   }
1199 }
1200
1201 static GstStateChangeReturn
1202 gst_ximagesink_change_state (GstElement * element, GstStateChange transition)
1203 {
1204   GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
1205   GstXImageSink *ximagesink;
1206   GstXContext *xcontext = NULL;
1207
1208   ximagesink = GST_XIMAGESINK (element);
1209
1210   switch (transition) {
1211     case GST_STATE_CHANGE_NULL_TO_READY:
1212       /* Initializing the XContext */
1213       if (ximagesink->xcontext == NULL) {
1214         xcontext = gst_ximagesink_xcontext_get (ximagesink);
1215         if (xcontext == NULL) {
1216           ret = GST_STATE_CHANGE_FAILURE;
1217           goto beach;
1218         }
1219         GST_OBJECT_LOCK (ximagesink);
1220         if (xcontext)
1221           ximagesink->xcontext = xcontext;
1222         GST_OBJECT_UNLOCK (ximagesink);
1223       }
1224
1225       /* call XSynchronize with the current value of synchronous */
1226       GST_DEBUG_OBJECT (ximagesink, "XSynchronize called with %s",
1227           ximagesink->synchronous ? "TRUE" : "FALSE");
1228       g_mutex_lock (ximagesink->x_lock);
1229       XSynchronize (ximagesink->xcontext->disp, ximagesink->synchronous);
1230       g_mutex_unlock (ximagesink->x_lock);
1231       gst_ximagesink_manage_event_thread (ximagesink);
1232       break;
1233     case GST_STATE_CHANGE_READY_TO_PAUSED:
1234       g_mutex_lock (ximagesink->flow_lock);
1235       if (ximagesink->xwindow)
1236         gst_ximagesink_xwindow_clear (ximagesink, ximagesink->xwindow);
1237       g_mutex_unlock (ximagesink->flow_lock);
1238       break;
1239     case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
1240       break;
1241     default:
1242       break;
1243   }
1244
1245   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
1246
1247   switch (transition) {
1248     case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
1249       break;
1250     case GST_STATE_CHANGE_PAUSED_TO_READY:
1251       ximagesink->fps_n = 0;
1252       ximagesink->fps_d = 1;
1253       GST_VIDEO_SINK_WIDTH (ximagesink) = 0;
1254       GST_VIDEO_SINK_HEIGHT (ximagesink) = 0;
1255       g_mutex_lock (ximagesink->flow_lock);
1256       if (ximagesink->pool)
1257         gst_buffer_pool_set_active (ximagesink->pool, FALSE);
1258       g_mutex_unlock (ximagesink->flow_lock);
1259       break;
1260     case GST_STATE_CHANGE_READY_TO_NULL:
1261       gst_ximagesink_reset (ximagesink);
1262       break;
1263     default:
1264       break;
1265   }
1266
1267 beach:
1268   return ret;
1269 }
1270
1271 static void
1272 gst_ximagesink_get_times (GstBaseSink * bsink, GstBuffer * buf,
1273     GstClockTime * start, GstClockTime * end)
1274 {
1275   GstXImageSink *ximagesink;
1276
1277   ximagesink = GST_XIMAGESINK (bsink);
1278
1279   if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
1280     *start = GST_BUFFER_TIMESTAMP (buf);
1281     if (GST_BUFFER_DURATION_IS_VALID (buf)) {
1282       *end = *start + GST_BUFFER_DURATION (buf);
1283     } else {
1284       if (ximagesink->fps_n > 0) {
1285         *end = *start +
1286             gst_util_uint64_scale_int (GST_SECOND, ximagesink->fps_d,
1287             ximagesink->fps_n);
1288       }
1289     }
1290   }
1291 }
1292
1293 static GstFlowReturn
1294 gst_ximagesink_show_frame (GstVideoSink * vsink, GstBuffer * buf)
1295 {
1296   GstFlowReturn res;
1297   GstXImageSink *ximagesink;
1298   GstXImageMeta *meta;
1299   GstBuffer *to_put = NULL;
1300
1301   ximagesink = GST_XIMAGESINK (vsink);
1302
1303   meta = gst_buffer_get_ximage_meta (buf);
1304
1305   if (meta && meta->sink == ximagesink) {
1306     /* If this buffer has been allocated using our buffer management we simply
1307        put the ximage which is in the PRIVATE pointer */
1308     GST_LOG_OBJECT (ximagesink, "buffer from our pool, writing directly");
1309     to_put = buf;
1310     res = GST_FLOW_OK;
1311   } else {
1312     GstVideoFrame src, dest;
1313
1314     /* Else we have to copy the data into our private image, */
1315     /* if we have one... */
1316     GST_LOG_OBJECT (ximagesink, "buffer not from our pool, copying");
1317
1318     /* we should have a pool, configured in setcaps */
1319     if (ximagesink->pool == NULL)
1320       goto no_pool;
1321
1322     if (!gst_buffer_pool_set_active (ximagesink->pool, TRUE))
1323       goto activate_failed;
1324
1325     /* take a buffer from our pool */
1326     res = gst_buffer_pool_acquire_buffer (ximagesink->pool, &to_put, NULL);
1327     if (res != GST_FLOW_OK)
1328       goto no_buffer;
1329
1330     GST_CAT_LOG_OBJECT (GST_CAT_PERFORMANCE, ximagesink,
1331         "slow copy into bufferpool buffer %p", to_put);
1332
1333     if (!gst_video_frame_map (&src, &ximagesink->info, buf, GST_MAP_READ))
1334       goto invalid_buffer;
1335
1336     if (!gst_video_frame_map (&dest, &ximagesink->info, to_put, GST_MAP_WRITE)) {
1337       gst_video_frame_unmap (&src);
1338       goto invalid_buffer;
1339     }
1340
1341     gst_video_frame_copy (&dest, &src);
1342
1343     gst_video_frame_unmap (&dest);
1344     gst_video_frame_unmap (&src);
1345   }
1346
1347   if (!gst_ximagesink_ximage_put (ximagesink, to_put))
1348     goto no_window;
1349
1350 done:
1351   if (to_put != buf)
1352     gst_buffer_unref (to_put);
1353
1354   return res;
1355
1356   /* ERRORS */
1357 no_pool:
1358   {
1359     GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
1360         ("Internal error: can't allocate images"),
1361         ("We don't have a bufferpool negotiated"));
1362     return GST_FLOW_ERROR;
1363   }
1364 no_buffer:
1365   {
1366     /* No image available. That's very bad ! */
1367     GST_WARNING_OBJECT (ximagesink, "could not create image");
1368     return res;
1369   }
1370 invalid_buffer:
1371   {
1372     /* No Window available to put our image into */
1373     GST_WARNING_OBJECT (ximagesink, "could map image");
1374     res = GST_FLOW_OK;
1375     goto done;
1376   }
1377 no_window:
1378   {
1379     /* No Window available to put our image into */
1380     GST_WARNING_OBJECT (ximagesink, "could not output image - no window");
1381     res = GST_FLOW_ERROR;
1382     goto done;
1383   }
1384 activate_failed:
1385   {
1386     GST_ERROR_OBJECT (ximagesink, "failed to activate bufferpool.");
1387     res = GST_FLOW_ERROR;
1388     goto done;
1389   }
1390 }
1391
1392 static gboolean
1393 gst_ximagesink_event (GstBaseSink * sink, GstEvent * event)
1394 {
1395   GstXImageSink *ximagesink = GST_XIMAGESINK (sink);
1396
1397   switch (GST_EVENT_TYPE (event)) {
1398     case GST_EVENT_TAG:{
1399       GstTagList *l;
1400       gchar *title = NULL;
1401
1402       gst_event_parse_tag (event, &l);
1403       gst_tag_list_get_string (l, GST_TAG_TITLE, &title);
1404
1405       if (title) {
1406         GST_DEBUG_OBJECT (ximagesink, "got tags, title='%s'", title);
1407         gst_ximagesink_xwindow_set_title (ximagesink, ximagesink->xwindow,
1408             title);
1409
1410         g_free (title);
1411       }
1412       break;
1413     }
1414     default:
1415       break;
1416   }
1417   return GST_BASE_SINK_CLASS (parent_class)->event (sink, event);
1418 }
1419
1420 static gboolean
1421 gst_ximagesink_propose_allocation (GstBaseSink * bsink, GstQuery * query)
1422 {
1423   GstXImageSink *ximagesink = GST_XIMAGESINK (bsink);
1424   GstBufferPool *pool;
1425   GstStructure *config;
1426   GstCaps *caps;
1427   guint size;
1428   gboolean need_pool;
1429
1430   gst_query_parse_allocation (query, &caps, &need_pool);
1431
1432   if (caps == NULL)
1433     goto no_caps;
1434
1435   g_mutex_lock (ximagesink->flow_lock);
1436   if ((pool = ximagesink->pool))
1437     gst_object_ref (pool);
1438   g_mutex_unlock (ximagesink->flow_lock);
1439
1440   if (pool != NULL) {
1441     const GstCaps *pcaps;
1442
1443     /* we had a pool, check caps */
1444     config = gst_buffer_pool_get_config (pool);
1445     gst_buffer_pool_config_get (config, &pcaps, &size, NULL, NULL, NULL, NULL,
1446         NULL);
1447
1448     GST_DEBUG_OBJECT (ximagesink,
1449         "we had a pool with caps %" GST_PTR_FORMAT, pcaps);
1450     if (!gst_caps_is_equal (caps, pcaps)) {
1451       /* different caps, we can't use this pool */
1452       GST_DEBUG_OBJECT (ximagesink, "pool has different caps");
1453       gst_object_unref (pool);
1454       pool = NULL;
1455     }
1456   }
1457   if (pool == NULL && need_pool) {
1458     GstVideoInfo info;
1459
1460     GST_DEBUG_OBJECT (ximagesink, "create new pool");
1461     pool = gst_ximage_buffer_pool_new (ximagesink);
1462
1463     if (!gst_video_info_from_caps (&info, caps))
1464       goto invalid_caps;
1465
1466     /* the normal size of a frame */
1467     size = info.size;
1468
1469     config = gst_buffer_pool_get_config (pool);
1470     gst_buffer_pool_config_set (config, caps, size, 0, 0, 0, 0, 0);
1471     if (!gst_buffer_pool_set_config (pool, config))
1472       goto config_failed;
1473   }
1474   /* we need at least 2 buffer because we hold on to the last one */
1475   gst_query_set_allocation_params (query, size, 2, 0, 0, 0, 0, pool);
1476
1477   /* we also support various metadata */
1478   gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE);
1479   gst_query_add_allocation_meta (query, GST_VIDEO_CROP_META_API_TYPE);
1480
1481   gst_object_unref (pool);
1482
1483   return TRUE;
1484
1485   /* ERRORS */
1486 no_caps:
1487   {
1488     GST_DEBUG_OBJECT (bsink, "no caps specified");
1489     return FALSE;
1490   }
1491 invalid_caps:
1492   {
1493     GST_DEBUG_OBJECT (bsink, "invalid caps specified");
1494     return FALSE;
1495   }
1496 config_failed:
1497   {
1498     GST_DEBUG_OBJECT (bsink, "failed setting config");
1499     return FALSE;
1500   }
1501 }
1502
1503 /* Interfaces stuff */
1504 static void
1505 gst_ximagesink_navigation_send_event (GstNavigation * navigation,
1506     GstStructure * structure)
1507 {
1508   GstXImageSink *ximagesink = GST_XIMAGESINK (navigation);
1509   GstEvent *event;
1510   gint x_offset, y_offset;
1511   gdouble x, y;
1512   GstPad *pad = NULL;
1513
1514   event = gst_event_new_navigation (structure);
1515
1516   /* We are not converting the pointer coordinates as there's no hardware
1517      scaling done here. The only possible scaling is done by videoscale and
1518      videoscale will have to catch those events and tranform the coordinates
1519      to match the applied scaling. So here we just add the offset if the image
1520      is centered in the window.  */
1521
1522   /* We take the flow_lock while we look at the window */
1523   g_mutex_lock (ximagesink->flow_lock);
1524
1525   if (!ximagesink->xwindow) {
1526     g_mutex_unlock (ximagesink->flow_lock);
1527     return;
1528   }
1529
1530   x_offset = ximagesink->xwindow->width - GST_VIDEO_SINK_WIDTH (ximagesink);
1531   y_offset = ximagesink->xwindow->height - GST_VIDEO_SINK_HEIGHT (ximagesink);
1532
1533   g_mutex_unlock (ximagesink->flow_lock);
1534
1535   if (x_offset > 0 && gst_structure_get_double (structure, "pointer_x", &x)) {
1536     x -= x_offset / 2;
1537     gst_structure_set (structure, "pointer_x", G_TYPE_DOUBLE, x, NULL);
1538   }
1539   if (y_offset > 0 && gst_structure_get_double (structure, "pointer_y", &y)) {
1540     y -= y_offset / 2;
1541     gst_structure_set (structure, "pointer_y", G_TYPE_DOUBLE, y, NULL);
1542   }
1543
1544   pad = gst_pad_get_peer (GST_VIDEO_SINK_PAD (ximagesink));
1545
1546   if (GST_IS_PAD (pad) && GST_IS_EVENT (event)) {
1547     gst_pad_send_event (pad, event);
1548
1549     gst_object_unref (pad);
1550   }
1551 }
1552
1553 static void
1554 gst_ximagesink_navigation_init (GstNavigationInterface * iface)
1555 {
1556   iface->send_event = gst_ximagesink_navigation_send_event;
1557 }
1558
1559 static void
1560 gst_ximagesink_set_window_handle (GstVideoOverlay * overlay, guintptr id)
1561 {
1562   XID xwindow_id = id;
1563   GstXImageSink *ximagesink = GST_XIMAGESINK (overlay);
1564   GstXWindow *xwindow = NULL;
1565   XWindowAttributes attr;
1566
1567   /* We acquire the stream lock while setting this window in the element.
1568      We are basically cleaning tons of stuff replacing the old window, putting
1569      images while we do that would surely crash */
1570   g_mutex_lock (ximagesink->flow_lock);
1571
1572   /* If we already use that window return */
1573   if (ximagesink->xwindow && (xwindow_id == ximagesink->xwindow->win)) {
1574     g_mutex_unlock (ximagesink->flow_lock);
1575     return;
1576   }
1577
1578   /* If the element has not initialized the X11 context try to do so */
1579   if (!ximagesink->xcontext &&
1580       !(ximagesink->xcontext = gst_ximagesink_xcontext_get (ximagesink))) {
1581     g_mutex_unlock (ximagesink->flow_lock);
1582     /* we have thrown a GST_ELEMENT_ERROR now */
1583     return;
1584   }
1585
1586   /* If a window is there already we destroy it */
1587   if (ximagesink->xwindow) {
1588     gst_ximagesink_xwindow_destroy (ximagesink, ximagesink->xwindow);
1589     ximagesink->xwindow = NULL;
1590   }
1591
1592   /* If the xid is 0 we go back to an internal window */
1593   if (xwindow_id == 0) {
1594     /* If no width/height caps nego did not happen window will be created
1595        during caps nego then */
1596     if (GST_VIDEO_SINK_WIDTH (ximagesink) && GST_VIDEO_SINK_HEIGHT (ximagesink)) {
1597       xwindow = gst_ximagesink_xwindow_new (ximagesink,
1598           GST_VIDEO_SINK_WIDTH (ximagesink),
1599           GST_VIDEO_SINK_HEIGHT (ximagesink));
1600     }
1601   } else {
1602     xwindow = g_new0 (GstXWindow, 1);
1603
1604     xwindow->win = xwindow_id;
1605
1606     /* We get window geometry, set the event we want to receive,
1607        and create a GC */
1608     g_mutex_lock (ximagesink->x_lock);
1609     XGetWindowAttributes (ximagesink->xcontext->disp, xwindow->win, &attr);
1610     xwindow->width = attr.width;
1611     xwindow->height = attr.height;
1612     xwindow->internal = FALSE;
1613     if (ximagesink->handle_events) {
1614       XSelectInput (ximagesink->xcontext->disp, xwindow->win, ExposureMask |
1615           StructureNotifyMask | PointerMotionMask | KeyPressMask |
1616           KeyReleaseMask);
1617     }
1618
1619     xwindow->gc = XCreateGC (ximagesink->xcontext->disp, xwindow->win, 0, NULL);
1620     g_mutex_unlock (ximagesink->x_lock);
1621   }
1622
1623   if (xwindow)
1624     ximagesink->xwindow = xwindow;
1625
1626   g_mutex_unlock (ximagesink->flow_lock);
1627 }
1628
1629 static void
1630 gst_ximagesink_expose (GstVideoOverlay * overlay)
1631 {
1632   GstXImageSink *ximagesink = GST_XIMAGESINK (overlay);
1633
1634   gst_ximagesink_xwindow_update_geometry (ximagesink);
1635   gst_ximagesink_ximage_put (ximagesink, NULL);
1636 }
1637
1638 static void
1639 gst_ximagesink_set_event_handling (GstVideoOverlay * overlay,
1640     gboolean handle_events)
1641 {
1642   GstXImageSink *ximagesink = GST_XIMAGESINK (overlay);
1643
1644   ximagesink->handle_events = handle_events;
1645
1646   g_mutex_lock (ximagesink->flow_lock);
1647
1648   if (G_UNLIKELY (!ximagesink->xwindow)) {
1649     g_mutex_unlock (ximagesink->flow_lock);
1650     return;
1651   }
1652
1653   g_mutex_lock (ximagesink->x_lock);
1654
1655   if (handle_events) {
1656     if (ximagesink->xwindow->internal) {
1657       XSelectInput (ximagesink->xcontext->disp, ximagesink->xwindow->win,
1658           ExposureMask | StructureNotifyMask | PointerMotionMask |
1659           KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask);
1660     } else {
1661       XSelectInput (ximagesink->xcontext->disp, ximagesink->xwindow->win,
1662           ExposureMask | StructureNotifyMask | PointerMotionMask |
1663           KeyPressMask | KeyReleaseMask);
1664     }
1665   } else {
1666     XSelectInput (ximagesink->xcontext->disp, ximagesink->xwindow->win, 0);
1667   }
1668
1669   g_mutex_unlock (ximagesink->x_lock);
1670
1671   g_mutex_unlock (ximagesink->flow_lock);
1672 }
1673
1674 static void
1675 gst_ximagesink_video_overlay_init (GstVideoOverlayInterface * iface)
1676 {
1677   iface->set_window_handle = gst_ximagesink_set_window_handle;
1678   iface->expose = gst_ximagesink_expose;
1679   iface->handle_events = gst_ximagesink_set_event_handling;
1680 }
1681
1682 /* =========================================== */
1683 /*                                             */
1684 /*              Init & Class init              */
1685 /*                                             */
1686 /* =========================================== */
1687
1688 static void
1689 gst_ximagesink_set_property (GObject * object, guint prop_id,
1690     const GValue * value, GParamSpec * pspec)
1691 {
1692   GstXImageSink *ximagesink;
1693
1694   g_return_if_fail (GST_IS_XIMAGESINK (object));
1695
1696   ximagesink = GST_XIMAGESINK (object);
1697
1698   switch (prop_id) {
1699     case PROP_DISPLAY:
1700       ximagesink->display_name = g_strdup (g_value_get_string (value));
1701       break;
1702     case PROP_SYNCHRONOUS:
1703       ximagesink->synchronous = g_value_get_boolean (value);
1704       if (ximagesink->xcontext) {
1705         GST_DEBUG_OBJECT (ximagesink, "XSynchronize called with %s",
1706             ximagesink->synchronous ? "TRUE" : "FALSE");
1707         g_mutex_lock (ximagesink->x_lock);
1708         XSynchronize (ximagesink->xcontext->disp, ximagesink->synchronous);
1709         g_mutex_unlock (ximagesink->x_lock);
1710       }
1711       break;
1712     case PROP_FORCE_ASPECT_RATIO:
1713       ximagesink->keep_aspect = g_value_get_boolean (value);
1714       break;
1715     case PROP_PIXEL_ASPECT_RATIO:
1716     {
1717       GValue *tmp;
1718
1719       tmp = g_new0 (GValue, 1);
1720       g_value_init (tmp, GST_TYPE_FRACTION);
1721
1722       if (!g_value_transform (value, tmp)) {
1723         GST_WARNING_OBJECT (ximagesink,
1724             "Could not transform string to aspect ratio");
1725         g_free (tmp);
1726       } else {
1727         GST_DEBUG_OBJECT (ximagesink, "set PAR to %d/%d",
1728             gst_value_get_fraction_numerator (tmp),
1729             gst_value_get_fraction_denominator (tmp));
1730         g_free (ximagesink->par);
1731         ximagesink->par = tmp;
1732       }
1733     }
1734       break;
1735     case PROP_HANDLE_EVENTS:
1736       gst_ximagesink_set_event_handling (GST_VIDEO_OVERLAY (ximagesink),
1737           g_value_get_boolean (value));
1738       gst_ximagesink_manage_event_thread (ximagesink);
1739       break;
1740     case PROP_HANDLE_EXPOSE:
1741       ximagesink->handle_expose = g_value_get_boolean (value);
1742       gst_ximagesink_manage_event_thread (ximagesink);
1743       break;
1744     default:
1745       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1746       break;
1747   }
1748 }
1749
1750 static void
1751 gst_ximagesink_get_property (GObject * object, guint prop_id,
1752     GValue * value, GParamSpec * pspec)
1753 {
1754   GstXImageSink *ximagesink;
1755
1756   g_return_if_fail (GST_IS_XIMAGESINK (object));
1757
1758   ximagesink = GST_XIMAGESINK (object);
1759
1760   switch (prop_id) {
1761     case PROP_DISPLAY:
1762       g_value_set_string (value, ximagesink->display_name);
1763       break;
1764     case PROP_SYNCHRONOUS:
1765       g_value_set_boolean (value, ximagesink->synchronous);
1766       break;
1767     case PROP_FORCE_ASPECT_RATIO:
1768       g_value_set_boolean (value, ximagesink->keep_aspect);
1769       break;
1770     case PROP_PIXEL_ASPECT_RATIO:
1771       if (ximagesink->par)
1772         g_value_transform (ximagesink->par, value);
1773       break;
1774     case PROP_HANDLE_EVENTS:
1775       g_value_set_boolean (value, ximagesink->handle_events);
1776       break;
1777     case PROP_HANDLE_EXPOSE:
1778       g_value_set_boolean (value, ximagesink->handle_expose);
1779       break;
1780     case PROP_WINDOW_WIDTH:
1781       if (ximagesink->xwindow)
1782         g_value_set_uint64 (value, ximagesink->xwindow->width);
1783       else
1784         g_value_set_uint64 (value, 0);
1785       break;
1786     case PROP_WINDOW_HEIGHT:
1787       if (ximagesink->xwindow)
1788         g_value_set_uint64 (value, ximagesink->xwindow->height);
1789       else
1790         g_value_set_uint64 (value, 0);
1791       break;
1792     default:
1793       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1794       break;
1795   }
1796 }
1797
1798 static void
1799 gst_ximagesink_reset (GstXImageSink * ximagesink)
1800 {
1801   GThread *thread;
1802
1803   GST_OBJECT_LOCK (ximagesink);
1804   ximagesink->running = FALSE;
1805   /* grab thread and mark it as NULL */
1806   thread = ximagesink->event_thread;
1807   ximagesink->event_thread = NULL;
1808   GST_OBJECT_UNLOCK (ximagesink);
1809
1810   /* Wait for our event thread to finish before we clean up our stuff. */
1811   if (thread)
1812     g_thread_join (thread);
1813
1814   if (ximagesink->cur_image) {
1815     gst_buffer_unref (ximagesink->cur_image);
1816     ximagesink->cur_image = NULL;
1817   }
1818
1819   g_mutex_lock (ximagesink->flow_lock);
1820
1821   if (ximagesink->pool) {
1822     gst_object_unref (ximagesink->pool);
1823     ximagesink->pool = NULL;
1824   }
1825
1826   if (ximagesink->xwindow) {
1827     gst_ximagesink_xwindow_clear (ximagesink, ximagesink->xwindow);
1828     gst_ximagesink_xwindow_destroy (ximagesink, ximagesink->xwindow);
1829     ximagesink->xwindow = NULL;
1830   }
1831   g_mutex_unlock (ximagesink->flow_lock);
1832
1833   gst_ximagesink_xcontext_clear (ximagesink);
1834 }
1835
1836 static void
1837 gst_ximagesink_finalize (GObject * object)
1838 {
1839   GstXImageSink *ximagesink;
1840
1841   ximagesink = GST_XIMAGESINK (object);
1842
1843   gst_ximagesink_reset (ximagesink);
1844
1845   if (ximagesink->display_name) {
1846     g_free (ximagesink->display_name);
1847     ximagesink->display_name = NULL;
1848   }
1849   if (ximagesink->par) {
1850     g_free (ximagesink->par);
1851     ximagesink->par = NULL;
1852   }
1853   if (ximagesink->x_lock) {
1854     g_mutex_free (ximagesink->x_lock);
1855     ximagesink->x_lock = NULL;
1856   }
1857   if (ximagesink->flow_lock) {
1858     g_mutex_free (ximagesink->flow_lock);
1859     ximagesink->flow_lock = NULL;
1860   }
1861
1862   g_free (ximagesink->media_title);
1863
1864   G_OBJECT_CLASS (parent_class)->finalize (object);
1865 }
1866
1867 static void
1868 gst_ximagesink_init (GstXImageSink * ximagesink)
1869 {
1870   ximagesink->display_name = NULL;
1871   ximagesink->xcontext = NULL;
1872   ximagesink->xwindow = NULL;
1873   ximagesink->cur_image = NULL;
1874
1875   ximagesink->event_thread = NULL;
1876   ximagesink->running = FALSE;
1877
1878   ximagesink->fps_n = 0;
1879   ximagesink->fps_d = 1;
1880
1881   ximagesink->x_lock = g_mutex_new ();
1882   ximagesink->flow_lock = g_mutex_new ();
1883
1884   ximagesink->par = NULL;
1885
1886   ximagesink->pool = NULL;
1887
1888   ximagesink->synchronous = FALSE;
1889   ximagesink->keep_aspect = FALSE;
1890   ximagesink->handle_events = TRUE;
1891   ximagesink->handle_expose = TRUE;
1892 }
1893
1894 static void
1895 gst_ximagesink_class_init (GstXImageSinkClass * klass)
1896 {
1897   GObjectClass *gobject_class;
1898   GstElementClass *gstelement_class;
1899   GstBaseSinkClass *gstbasesink_class;
1900   GstVideoSinkClass *videosink_class;
1901
1902   gobject_class = (GObjectClass *) klass;
1903   gstelement_class = (GstElementClass *) klass;
1904   gstbasesink_class = (GstBaseSinkClass *) klass;
1905   videosink_class = (GstVideoSinkClass *) klass;
1906
1907   gobject_class->finalize = gst_ximagesink_finalize;
1908   gobject_class->set_property = gst_ximagesink_set_property;
1909   gobject_class->get_property = gst_ximagesink_get_property;
1910
1911   g_object_class_install_property (gobject_class, PROP_DISPLAY,
1912       g_param_spec_string ("display", "Display", "X Display name",
1913           NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1914   g_object_class_install_property (gobject_class, PROP_SYNCHRONOUS,
1915       g_param_spec_boolean ("synchronous", "Synchronous",
1916           "When enabled, runs the X display in synchronous mode. "
1917           "(unrelated to A/V sync, used only for debugging)", FALSE,
1918           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1919   g_object_class_install_property (gobject_class, PROP_FORCE_ASPECT_RATIO,
1920       g_param_spec_boolean ("force-aspect-ratio", "Force aspect ratio",
1921           "When enabled, reverse caps negotiation (scaling) will respect "
1922           "original aspect ratio", FALSE,
1923           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1924   g_object_class_install_property (gobject_class, PROP_PIXEL_ASPECT_RATIO,
1925       g_param_spec_string ("pixel-aspect-ratio", "Pixel Aspect Ratio",
1926           "The pixel aspect ratio of the device", "1/1",
1927           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1928   g_object_class_install_property (gobject_class, PROP_HANDLE_EVENTS,
1929       g_param_spec_boolean ("handle-events", "Handle XEvents",
1930           "When enabled, XEvents will be selected and handled", TRUE,
1931           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1932   g_object_class_install_property (gobject_class, PROP_HANDLE_EXPOSE,
1933       g_param_spec_boolean ("handle-expose", "Handle expose",
1934           "When enabled, "
1935           "the current frame will always be drawn in response to X Expose "
1936           "events", TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1937
1938   /**
1939    * GstXImageSink:window-width
1940    *
1941    * Actual width of the video window.
1942    *
1943    * Since: 0.10.32
1944    */
1945   g_object_class_install_property (gobject_class, PROP_WINDOW_WIDTH,
1946       g_param_spec_uint64 ("window-width", "window-width",
1947           "Width of the window", 0, G_MAXUINT64, 0,
1948           G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
1949
1950   /**
1951    * GstXImageSink:window-height
1952    *
1953    * Actual height of the video window.
1954    *
1955    * Since: 0.10.32
1956    */
1957   g_object_class_install_property (gobject_class, PROP_WINDOW_HEIGHT,
1958       g_param_spec_uint64 ("window-height", "window-height",
1959           "Height of the window", 0, G_MAXUINT64, 0,
1960           G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
1961
1962   gst_element_class_set_details_simple (gstelement_class,
1963       "Video sink", "Sink/Video",
1964       "A standard X based videosink", "Julien Moutte <julien@moutte.net>");
1965
1966   gst_element_class_add_pad_template (gstelement_class,
1967       gst_static_pad_template_get (&gst_ximagesink_sink_template_factory));
1968
1969   gstelement_class->change_state = gst_ximagesink_change_state;
1970
1971   gstbasesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_ximagesink_getcaps);
1972   gstbasesink_class->set_caps = GST_DEBUG_FUNCPTR (gst_ximagesink_setcaps);
1973   gstbasesink_class->get_times = GST_DEBUG_FUNCPTR (gst_ximagesink_get_times);
1974   gstbasesink_class->propose_allocation =
1975       GST_DEBUG_FUNCPTR (gst_ximagesink_propose_allocation);
1976   gstbasesink_class->event = GST_DEBUG_FUNCPTR (gst_ximagesink_event);
1977
1978   videosink_class->show_frame = GST_DEBUG_FUNCPTR (gst_ximagesink_show_frame);
1979 }