x11: fix typos
[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 GST_DEBUG_CATEGORY_EXTERN (gst_debug_ximagesink);
121 GST_DEBUG_CATEGORY_EXTERN (GST_CAT_PERFORMANCE);
122 #define GST_CAT_DEFAULT gst_debug_ximagesink
123
124 typedef struct
125 {
126   unsigned long flags;
127   unsigned long functions;
128   unsigned long decorations;
129   long input_mode;
130   unsigned long status;
131 }
132 MotifWmHints, MwmHints;
133
134 #define MWM_HINTS_DECORATIONS   (1L << 1)
135
136 static void gst_ximagesink_reset (GstXImageSink * ximagesink);
137 static void gst_ximagesink_xwindow_update_geometry (GstXImageSink * ximagesink);
138 static void gst_ximagesink_expose (GstVideoOverlay * overlay);
139
140 static GstStaticPadTemplate gst_ximagesink_sink_template_factory =
141 GST_STATIC_PAD_TEMPLATE ("sink",
142     GST_PAD_SINK,
143     GST_PAD_ALWAYS,
144     GST_STATIC_CAPS ("video/x-raw, "
145         "framerate = (fraction) [ 0, MAX ], "
146         "width = (int) [ 1, MAX ], " "height = (int) [ 1, MAX ]")
147     );
148
149 enum
150 {
151   PROP_0,
152   PROP_DISPLAY,
153   PROP_SYNCHRONOUS,
154   PROP_PIXEL_ASPECT_RATIO,
155   PROP_FORCE_ASPECT_RATIO,
156   PROP_HANDLE_EVENTS,
157   PROP_HANDLE_EXPOSE,
158   PROP_WINDOW_WIDTH,
159   PROP_WINDOW_HEIGHT
160 };
161
162 /* ============================================================= */
163 /*                                                               */
164 /*                       Public Methods                          */
165 /*                                                               */
166 /* ============================================================= */
167
168 /* =========================================== */
169 /*                                             */
170 /*          Object typing & Creation           */
171 /*                                             */
172 /* =========================================== */
173 static void gst_ximagesink_navigation_init (GstNavigationInterface * iface);
174 static void gst_ximagesink_video_overlay_init (GstVideoOverlayInterface *
175     iface);
176 #define gst_ximagesink_parent_class parent_class
177 G_DEFINE_TYPE_WITH_CODE (GstXImageSink, gst_ximagesink, GST_TYPE_VIDEO_SINK,
178     G_IMPLEMENT_INTERFACE (GST_TYPE_NAVIGATION, gst_ximagesink_navigation_init);
179     G_IMPLEMENT_INTERFACE (GST_TYPE_VIDEO_OVERLAY,
180         gst_ximagesink_video_overlay_init));
181
182 /* ============================================================= */
183 /*                                                               */
184 /*                       Private Methods                         */
185 /*                                                               */
186 /* ============================================================= */
187
188 /* X11 stuff */
189
190 /* We are called with the x_lock taken */
191 static void
192 gst_ximagesink_xwindow_draw_borders (GstXImageSink * ximagesink,
193     GstXWindow * xwindow, GstVideoRectangle rect)
194 {
195   g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
196   g_return_if_fail (xwindow != NULL);
197
198   XSetForeground (ximagesink->xcontext->disp, xwindow->gc,
199       ximagesink->xcontext->black);
200
201   /* Left border */
202   if (rect.x > 0) {
203     XFillRectangle (ximagesink->xcontext->disp, xwindow->win, xwindow->gc,
204         0, 0, rect.x, xwindow->height);
205   }
206
207   /* Right border */
208   if ((rect.x + rect.w) < xwindow->width) {
209     XFillRectangle (ximagesink->xcontext->disp, xwindow->win, xwindow->gc,
210         rect.x + rect.w, 0, xwindow->width, xwindow->height);
211   }
212
213   /* Top border */
214   if (rect.y > 0) {
215     XFillRectangle (ximagesink->xcontext->disp, xwindow->win, xwindow->gc,
216         0, 0, xwindow->width, rect.y);
217   }
218
219   /* Bottom border */
220   if ((rect.y + rect.h) < xwindow->height) {
221     XFillRectangle (ximagesink->xcontext->disp, xwindow->win, xwindow->gc,
222         0, rect.y + rect.h, xwindow->width, xwindow->height);
223   }
224 }
225
226 /* This function puts a GstXImageBuffer on a GstXImageSink's window */
227 static gboolean
228 gst_ximagesink_ximage_put (GstXImageSink * ximagesink, GstBuffer * ximage)
229 {
230   GstXImageMeta *meta;
231   GstVideoCropMeta *crop;
232   GstVideoRectangle src, dst, result;
233   gboolean draw_border = FALSE;
234
235   /* We take the flow_lock. If expose is in there we don't want to run
236      concurrently from the data flow thread */
237   g_mutex_lock (ximagesink->flow_lock);
238
239   if (G_UNLIKELY (ximagesink->xwindow == NULL)) {
240     g_mutex_unlock (ximagesink->flow_lock);
241     return FALSE;
242   }
243
244   /* Draw borders when displaying the first frame. After this
245      draw borders only on expose event or caps change (ximagesink->draw_border = TRUE). */
246   if (!ximagesink->cur_image || ximagesink->draw_border) {
247     draw_border = TRUE;
248   }
249
250   /* Store a reference to the last image we put, lose the previous one */
251   if (ximage && ximagesink->cur_image != ximage) {
252     if (ximagesink->cur_image) {
253       GST_LOG_OBJECT (ximagesink, "unreffing %p", ximagesink->cur_image);
254       gst_buffer_unref (ximagesink->cur_image);
255     }
256     GST_LOG_OBJECT (ximagesink, "reffing %p as our current image", ximage);
257     ximagesink->cur_image = gst_buffer_ref (ximage);
258   }
259
260   /* Expose sends a NULL image, we take the latest frame */
261   if (!ximage) {
262     draw_border = TRUE;
263     if (ximagesink->cur_image) {
264       ximage = ximagesink->cur_image;
265     } else {
266       g_mutex_unlock (ximagesink->flow_lock);
267       return TRUE;
268     }
269   }
270
271   meta = gst_buffer_get_ximage_meta (ximage);
272   crop = gst_buffer_get_video_crop_meta (ximage);
273
274   if (crop) {
275     src.x = crop->x + meta->x;
276     src.y = crop->y + meta->y;
277     src.w = crop->width;
278     src.h = crop->height;
279     GST_LOG_OBJECT (ximagesink,
280         "crop %dx%d-%dx%d", crop->x, crop->y, crop->width, crop->height);
281   } else {
282     src.x = meta->x;
283     src.y = meta->y;
284     src.w = meta->width;
285     src.h = meta->height;
286   }
287   dst.w = ximagesink->xwindow->width;
288   dst.h = ximagesink->xwindow->height;
289
290   gst_video_sink_center_rect (src, dst, &result, FALSE);
291
292   g_mutex_lock (ximagesink->x_lock);
293
294   if (draw_border) {
295     gst_ximagesink_xwindow_draw_borders (ximagesink, ximagesink->xwindow,
296         result);
297     ximagesink->draw_border = FALSE;
298   }
299 #ifdef HAVE_XSHM
300   if (ximagesink->xcontext->use_xshm) {
301     GST_LOG_OBJECT (ximagesink,
302         "XShmPutImage on %p, src: %d, %d - dest: %d, %d, dim: %dx%d, win %dx%d",
303         ximage, 0, 0, result.x, result.y, result.w, result.h,
304         ximagesink->xwindow->width, ximagesink->xwindow->height);
305     XShmPutImage (ximagesink->xcontext->disp, ximagesink->xwindow->win,
306         ximagesink->xwindow->gc, meta->ximage, src.x, src.y, result.x, result.y,
307         result.w, result.h, FALSE);
308   } else
309 #endif /* HAVE_XSHM */
310   {
311     GST_LOG_OBJECT (ximagesink,
312         "XPutImage on %p, src: %d, %d - dest: %d, %d, dim: %dx%d, win %dx%d",
313         ximage, 0, 0, result.x, result.y, result.w, result.h,
314         ximagesink->xwindow->width, ximagesink->xwindow->height);
315     XPutImage (ximagesink->xcontext->disp, ximagesink->xwindow->win,
316         ximagesink->xwindow->gc, meta->ximage, src.x, src.y, result.x, result.y,
317         result.w, result.h);
318   }
319
320   XSync (ximagesink->xcontext->disp, FALSE);
321
322   g_mutex_unlock (ximagesink->x_lock);
323
324   g_mutex_unlock (ximagesink->flow_lock);
325
326   return TRUE;
327 }
328
329 static gboolean
330 gst_ximagesink_xwindow_decorate (GstXImageSink * ximagesink,
331     GstXWindow * window)
332 {
333   Atom hints_atom = None;
334   MotifWmHints *hints;
335
336   g_return_val_if_fail (GST_IS_XIMAGESINK (ximagesink), FALSE);
337   g_return_val_if_fail (window != NULL, FALSE);
338
339   g_mutex_lock (ximagesink->x_lock);
340
341   hints_atom = XInternAtom (ximagesink->xcontext->disp, "_MOTIF_WM_HINTS",
342       True);
343   if (hints_atom == None) {
344     g_mutex_unlock (ximagesink->x_lock);
345     return FALSE;
346   }
347
348   hints = g_malloc0 (sizeof (MotifWmHints));
349
350   hints->flags |= MWM_HINTS_DECORATIONS;
351   hints->decorations = 1 << 0;
352
353   XChangeProperty (ximagesink->xcontext->disp, window->win,
354       hints_atom, hints_atom, 32, PropModeReplace,
355       (guchar *) hints, sizeof (MotifWmHints) / sizeof (long));
356
357   XSync (ximagesink->xcontext->disp, FALSE);
358
359   g_mutex_unlock (ximagesink->x_lock);
360
361   g_free (hints);
362
363   return TRUE;
364 }
365
366 static void
367 gst_ximagesink_xwindow_set_title (GstXImageSink * ximagesink,
368     GstXWindow * xwindow, const gchar * media_title)
369 {
370   if (media_title) {
371     g_free (ximagesink->media_title);
372     ximagesink->media_title = g_strdup (media_title);
373   }
374   if (xwindow) {
375     /* we have a window */
376     if (xwindow->internal) {
377       XTextProperty xproperty;
378       const gchar *app_name;
379       const gchar *title = NULL;
380       gchar *title_mem = NULL;
381
382       /* set application name as a title */
383       app_name = g_get_application_name ();
384
385       if (app_name && ximagesink->media_title) {
386         title = title_mem = g_strconcat (ximagesink->media_title, " : ",
387             app_name, NULL);
388       } else if (app_name) {
389         title = app_name;
390       } else if (ximagesink->media_title) {
391         title = ximagesink->media_title;
392       }
393
394       if (title) {
395         if ((XStringListToTextProperty (((char **) &title), 1,
396                     &xproperty)) != 0) {
397           XSetWMName (ximagesink->xcontext->disp, xwindow->win, &xproperty);
398           XFree (xproperty.value);
399         }
400
401         g_free (title_mem);
402       }
403     }
404   }
405 }
406
407 /* This function handles a GstXWindow creation */
408 static GstXWindow *
409 gst_ximagesink_xwindow_new (GstXImageSink * ximagesink, gint width, gint height)
410 {
411   GstXWindow *xwindow = NULL;
412   XGCValues values;
413
414   g_return_val_if_fail (GST_IS_XIMAGESINK (ximagesink), NULL);
415
416   xwindow = g_new0 (GstXWindow, 1);
417
418   xwindow->width = width;
419   xwindow->height = height;
420   xwindow->internal = TRUE;
421
422   g_mutex_lock (ximagesink->x_lock);
423
424   xwindow->win = XCreateSimpleWindow (ximagesink->xcontext->disp,
425       ximagesink->xcontext->root,
426       0, 0, width, height, 0, 0, ximagesink->xcontext->black);
427
428   /* We have to do that to prevent X from redrawing the background on 
429      ConfigureNotify. This takes away flickering of video when resizing. */
430   XSetWindowBackgroundPixmap (ximagesink->xcontext->disp, xwindow->win, None);
431
432   /* set application name as a title */
433   gst_ximagesink_xwindow_set_title (ximagesink, xwindow, NULL);
434
435   if (ximagesink->handle_events) {
436     Atom wm_delete;
437
438     XSelectInput (ximagesink->xcontext->disp, xwindow->win, ExposureMask |
439         StructureNotifyMask | PointerMotionMask | KeyPressMask |
440         KeyReleaseMask | ButtonPressMask | ButtonReleaseMask);
441
442     /* Tell the window manager we'd like delete client messages instead of
443      * being killed */
444     wm_delete = XInternAtom (ximagesink->xcontext->disp,
445         "WM_DELETE_WINDOW", False);
446     (void) XSetWMProtocols (ximagesink->xcontext->disp, xwindow->win,
447         &wm_delete, 1);
448   }
449
450   xwindow->gc = XCreateGC (ximagesink->xcontext->disp, xwindow->win,
451       0, &values);
452
453   XMapRaised (ximagesink->xcontext->disp, xwindow->win);
454
455   XSync (ximagesink->xcontext->disp, FALSE);
456
457   g_mutex_unlock (ximagesink->x_lock);
458
459   gst_ximagesink_xwindow_decorate (ximagesink, xwindow);
460
461   gst_video_overlay_got_window_handle (GST_VIDEO_OVERLAY (ximagesink),
462       xwindow->win);
463
464   return xwindow;
465 }
466
467 /* This function destroys a GstXWindow */
468 static void
469 gst_ximagesink_xwindow_destroy (GstXImageSink * ximagesink,
470     GstXWindow * xwindow)
471 {
472   g_return_if_fail (xwindow != NULL);
473   g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
474
475   g_mutex_lock (ximagesink->x_lock);
476
477   /* If we did not create that window we just free the GC and let it live */
478   if (xwindow->internal)
479     XDestroyWindow (ximagesink->xcontext->disp, xwindow->win);
480   else
481     XSelectInput (ximagesink->xcontext->disp, xwindow->win, 0);
482
483   XFreeGC (ximagesink->xcontext->disp, xwindow->gc);
484
485   XSync (ximagesink->xcontext->disp, FALSE);
486
487   g_mutex_unlock (ximagesink->x_lock);
488
489   g_free (xwindow);
490 }
491
492 static void
493 gst_ximagesink_xwindow_update_geometry (GstXImageSink * ximagesink)
494 {
495   XWindowAttributes attr;
496   gboolean reconfigure;
497
498   g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
499
500   /* Update the window geometry */
501   g_mutex_lock (ximagesink->x_lock);
502   if (G_UNLIKELY (ximagesink->xwindow == NULL)) {
503     g_mutex_unlock (ximagesink->x_lock);
504     return;
505   }
506
507   XGetWindowAttributes (ximagesink->xcontext->disp,
508       ximagesink->xwindow->win, &attr);
509
510   /* Check if we would suggest a different width/height now */
511   reconfigure = (ximagesink->xwindow->width != attr.width)
512       || (ximagesink->xwindow->height != attr.height);
513   ximagesink->xwindow->width = attr.width;
514   ximagesink->xwindow->height = attr.height;
515
516   g_mutex_unlock (ximagesink->x_lock);
517
518   if (reconfigure)
519     gst_pad_push_event (GST_BASE_SINK (ximagesink)->sinkpad,
520         gst_event_new_reconfigure ());
521 }
522
523 static void
524 gst_ximagesink_xwindow_clear (GstXImageSink * ximagesink, GstXWindow * xwindow)
525 {
526   g_return_if_fail (xwindow != NULL);
527   g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
528
529   g_mutex_lock (ximagesink->x_lock);
530
531   XSetForeground (ximagesink->xcontext->disp, xwindow->gc,
532       ximagesink->xcontext->black);
533
534   XFillRectangle (ximagesink->xcontext->disp, xwindow->win, xwindow->gc,
535       0, 0, xwindow->width, xwindow->height);
536
537   XSync (ximagesink->xcontext->disp, FALSE);
538
539   g_mutex_unlock (ximagesink->x_lock);
540 }
541
542 /* This function handles XEvents that might be in the queue. It generates
543    GstEvent that will be sent upstream in the pipeline to handle interactivity
544    and navigation.*/
545 static void
546 gst_ximagesink_handle_xevents (GstXImageSink * ximagesink)
547 {
548   XEvent e;
549   guint pointer_x = 0, pointer_y = 0;
550   gboolean pointer_moved = FALSE;
551   gboolean exposed = FALSE, configured = FALSE;
552
553   g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
554
555   /* Then we get all pointer motion events, only the last position is
556      interesting. */
557   g_mutex_lock (ximagesink->flow_lock);
558   g_mutex_lock (ximagesink->x_lock);
559   while (XCheckWindowEvent (ximagesink->xcontext->disp,
560           ximagesink->xwindow->win, PointerMotionMask, &e)) {
561     g_mutex_unlock (ximagesink->x_lock);
562     g_mutex_unlock (ximagesink->flow_lock);
563
564     switch (e.type) {
565       case MotionNotify:
566         pointer_x = e.xmotion.x;
567         pointer_y = e.xmotion.y;
568         pointer_moved = TRUE;
569         break;
570       default:
571         break;
572     }
573     g_mutex_lock (ximagesink->flow_lock);
574     g_mutex_lock (ximagesink->x_lock);
575   }
576
577   if (pointer_moved) {
578     g_mutex_unlock (ximagesink->x_lock);
579     g_mutex_unlock (ximagesink->flow_lock);
580
581     GST_DEBUG ("ximagesink pointer moved over window at %d,%d",
582         pointer_x, pointer_y);
583     gst_navigation_send_mouse_event (GST_NAVIGATION (ximagesink),
584         "mouse-move", 0, pointer_x, pointer_y);
585
586     g_mutex_lock (ximagesink->flow_lock);
587     g_mutex_lock (ximagesink->x_lock);
588   }
589
590   /* We get all remaining events on our window to throw them upstream */
591   while (XCheckWindowEvent (ximagesink->xcontext->disp,
592           ximagesink->xwindow->win,
593           KeyPressMask | KeyReleaseMask |
594           ButtonPressMask | ButtonReleaseMask, &e)) {
595     KeySym keysym;
596
597     /* We lock only for the X function call */
598     g_mutex_unlock (ximagesink->x_lock);
599     g_mutex_unlock (ximagesink->flow_lock);
600
601     switch (e.type) {
602       case ButtonPress:
603         /* Mouse button pressed/released over our window. We send upstream
604            events for interactivity/navigation */
605         GST_DEBUG ("ximagesink button %d pressed over window at %d,%d",
606             e.xbutton.button, e.xbutton.x, e.xbutton.x);
607         gst_navigation_send_mouse_event (GST_NAVIGATION (ximagesink),
608             "mouse-button-press", e.xbutton.button, e.xbutton.x, e.xbutton.y);
609         break;
610       case ButtonRelease:
611         GST_DEBUG ("ximagesink button %d release over window at %d,%d",
612             e.xbutton.button, e.xbutton.x, e.xbutton.x);
613         gst_navigation_send_mouse_event (GST_NAVIGATION (ximagesink),
614             "mouse-button-release", e.xbutton.button, e.xbutton.x, e.xbutton.y);
615         break;
616       case KeyPress:
617       case KeyRelease:
618         /* Key pressed/released over our window. We send upstream
619            events for interactivity/navigation */
620         GST_DEBUG ("ximagesink key %d pressed over window at %d,%d",
621             e.xkey.keycode, e.xkey.x, e.xkey.x);
622         g_mutex_lock (ximagesink->x_lock);
623         keysym = XKeycodeToKeysym (ximagesink->xcontext->disp,
624             e.xkey.keycode, 0);
625         g_mutex_unlock (ximagesink->x_lock);
626         if (keysym != NoSymbol) {
627           char *key_str = NULL;
628
629           g_mutex_lock (ximagesink->x_lock);
630           key_str = XKeysymToString (keysym);
631           g_mutex_unlock (ximagesink->x_lock);
632           gst_navigation_send_key_event (GST_NAVIGATION (ximagesink),
633               e.type == KeyPress ? "key-press" : "key-release", key_str);
634         } else {
635           gst_navigation_send_key_event (GST_NAVIGATION (ximagesink),
636               e.type == KeyPress ? "key-press" : "key-release", "unknown");
637         }
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 }