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