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