Set _NET_WM_NAME property for xvimagesink and ximagesink
[platform/upstream/gstreamer.git] / sys / xvimage / xvcontext.c
1 /* GStreamer
2  * Copyright (C) <2005> Julien Moutte <julien@moutte.net>
3  *               <2009>,<2010> Stefan Kost <stefan.kost@nokia.com>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
18  * Boston, MA 02110-1301, USA.
19  */
20
21 /* for developers: there are two useful tools : xvinfo and xvattr */
22
23 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif
26
27 /* Object header */
28 #include "xvcontext.h"
29
30 /* Debugging category */
31 #include <gst/gstinfo.h>
32
33 /* for XkbKeycodeToKeysym */
34 #include <X11/XKBlib.h>
35
36 GST_DEBUG_CATEGORY_EXTERN (gst_debug_xv_context);
37 #define GST_CAT_DEFAULT gst_debug_xv_context
38
39 void
40 gst_xvcontext_config_clear (GstXvContextConfig * config)
41 {
42   if (config->display_name) {
43     g_free (config->display_name);
44     config->display_name = NULL;
45   }
46   config->adaptor_nr = -1;
47 }
48
49 GST_DEFINE_MINI_OBJECT_TYPE (GstXvContext, gst_xvcontext);
50
51 typedef struct
52 {
53   unsigned long flags;
54   unsigned long functions;
55   unsigned long decorations;
56   long input_mode;
57   unsigned long status;
58 }
59 MotifWmHints, MwmHints;
60
61 #define MWM_HINTS_DECORATIONS   (1L << 1)
62
63
64 static void
65 gst_lookup_xv_port_from_adaptor (GstXvContext * context,
66     XvAdaptorInfo * adaptors, guint adaptor_nr)
67 {
68   gint j;
69   gint res;
70
71   /* Do we support XvImageMask ? */
72   if (!(adaptors[adaptor_nr].type & XvImageMask)) {
73     GST_DEBUG ("XV Adaptor %s has no support for XvImageMask",
74         adaptors[adaptor_nr].name);
75     return;
76   }
77
78   /* We found such an adaptor, looking for an available port */
79   for (j = 0; j < adaptors[adaptor_nr].num_ports && !context->xv_port_id; j++) {
80     /* We try to grab the port */
81     res = XvGrabPort (context->disp, adaptors[adaptor_nr].base_id + j, 0);
82     if (Success == res) {
83       context->xv_port_id = adaptors[adaptor_nr].base_id + j;
84       GST_DEBUG ("XV Adaptor %s with %ld ports", adaptors[adaptor_nr].name,
85           adaptors[adaptor_nr].num_ports);
86     } else {
87       GST_DEBUG ("GrabPort %d for XV Adaptor %s failed: %d", j,
88           adaptors[adaptor_nr].name, res);
89     }
90   }
91 }
92
93 /* This function generates a caps with all supported format by the first
94    Xv grabable port we find. We store each one of the supported formats in a
95    format list and append the format to a newly created caps that we return
96    If this function does not return NULL because of an error, it also grabs
97    the port via XvGrabPort */
98 static GstCaps *
99 gst_xvcontext_get_xv_support (GstXvContext * context,
100     const GstXvContextConfig * config, GError ** error)
101 {
102   gint i;
103   XvAdaptorInfo *adaptors;
104   gint nb_formats;
105   XvImageFormatValues *formats = NULL;
106   guint nb_encodings;
107   XvEncodingInfo *encodings = NULL;
108   gulong max_w = G_MAXINT, max_h = G_MAXINT;
109   GstCaps *caps = NULL;
110   GstCaps *rgb_caps = NULL;
111
112   g_return_val_if_fail (context != NULL, NULL);
113
114   /* First let's check that XVideo extension is available */
115   if (!XQueryExtension (context->disp, "XVideo", &i, &i, &i))
116     goto no_xv;
117
118   /* Then we get adaptors list */
119   if (Success != XvQueryAdaptors (context->disp, context->root,
120           &context->nb_adaptors, &adaptors))
121     goto no_adaptors;
122
123   context->xv_port_id = 0;
124
125   GST_DEBUG ("Found %u XV adaptor(s)", context->nb_adaptors);
126
127   context->adaptors =
128       (gchar **) g_malloc0 (context->nb_adaptors * sizeof (gchar *));
129
130   /* Now fill up our adaptor name array */
131   for (i = 0; i < context->nb_adaptors; i++) {
132     context->adaptors[i] = g_strdup (adaptors[i].name);
133   }
134
135   if (config->adaptor_nr != -1 && config->adaptor_nr < context->nb_adaptors) {
136     /* Find xv port from user defined adaptor */
137     gst_lookup_xv_port_from_adaptor (context, adaptors, config->adaptor_nr);
138   }
139
140   if (!context->xv_port_id) {
141     /* Now search for an adaptor that supports XvImageMask */
142     for (i = 0; i < context->nb_adaptors && !context->xv_port_id; i++) {
143       gst_lookup_xv_port_from_adaptor (context, adaptors, i);
144       context->adaptor_nr = i;
145     }
146   }
147
148   XvFreeAdaptorInfo (adaptors);
149
150   if (!context->xv_port_id)
151     goto no_ports;
152
153   /* Set XV_AUTOPAINT_COLORKEY and XV_DOUBLE_BUFFER and XV_COLORKEY */
154   {
155     int count, todo = 4;
156     XvAttribute *const attr = XvQueryPortAttributes (context->disp,
157         context->xv_port_id, &count);
158     static const char autopaint[] = "XV_AUTOPAINT_COLORKEY";
159     static const char dbl_buffer[] = "XV_DOUBLE_BUFFER";
160     static const char colorkey[] = "XV_COLORKEY";
161     static const char iturbt709[] = "XV_ITURBT_709";
162     static const char *xv_colorspace = "XV_COLORSPACE";
163
164     GST_DEBUG ("Checking %d Xv port attributes", count);
165
166     context->have_autopaint_colorkey = FALSE;
167     context->have_double_buffer = FALSE;
168     context->have_colorkey = FALSE;
169     context->have_iturbt709 = FALSE;
170     context->have_xvcolorspace = FALSE;
171
172     for (i = 0; ((i < count) && todo); i++) {
173       GST_DEBUG ("Got attribute %s", attr[i].name);
174
175       if (!strcmp (attr[i].name, autopaint)) {
176         const Atom atom = XInternAtom (context->disp, autopaint, False);
177
178         /* turn on autopaint colorkey */
179         XvSetPortAttribute (context->disp, context->xv_port_id, atom,
180             (config->autopaint_colorkey ? 1 : 0));
181         todo--;
182         context->have_autopaint_colorkey = TRUE;
183       } else if (!strcmp (attr[i].name, dbl_buffer)) {
184         const Atom atom = XInternAtom (context->disp, dbl_buffer, False);
185
186         XvSetPortAttribute (context->disp, context->xv_port_id, atom,
187             (config->double_buffer ? 1 : 0));
188         todo--;
189         context->have_double_buffer = TRUE;
190       } else if (!strcmp (attr[i].name, colorkey)) {
191         /* Set the colorkey, default is something that is dark but hopefully
192          * won't randomly appear on the screen elsewhere (ie not black or greys)
193          * can be overridden by setting "colorkey" property
194          */
195         const Atom atom = XInternAtom (context->disp, colorkey, False);
196         guint32 ckey = 0;
197         gboolean set_attr = TRUE;
198         guint cr, cg, cb;
199
200         /* set a colorkey in the right format RGB565/RGB888
201          * We only handle these 2 cases, because they're the only types of
202          * devices we've encountered. If we don't recognise it, leave it alone
203          */
204         cr = (config->colorkey >> 16);
205         cg = (config->colorkey >> 8) & 0xFF;
206         cb = (config->colorkey) & 0xFF;
207         switch (context->depth) {
208           case 16:             /* RGB 565 */
209             cr >>= 3;
210             cg >>= 2;
211             cb >>= 3;
212             ckey = (cr << 11) | (cg << 5) | cb;
213             break;
214           case 24:
215           case 32:             /* RGB 888 / ARGB 8888 */
216             ckey = (cr << 16) | (cg << 8) | cb;
217             break;
218           default:
219             GST_DEBUG ("Unknown bit depth %d for Xv Colorkey - not adjusting",
220                 context->depth);
221             set_attr = FALSE;
222             break;
223         }
224
225         if (set_attr) {
226           ckey = CLAMP (ckey, (guint32) attr[i].min_value,
227               (guint32) attr[i].max_value);
228           GST_LOG ("Setting color key for display depth %d to 0x%x",
229               context->depth, ckey);
230
231           XvSetPortAttribute (context->disp, context->xv_port_id, atom,
232               (gint) ckey);
233         }
234         todo--;
235         context->have_colorkey = TRUE;
236       } else if (!strcmp (attr[i].name, iturbt709)) {
237         todo--;
238         context->have_iturbt709 = TRUE;
239       } else if (!strcmp (attr[i].name, xv_colorspace)) {
240         context->have_xvcolorspace = TRUE;
241         todo--;
242       }
243     }
244
245     XFree (attr);
246   }
247
248   /* Get the list of encodings supported by the adapter and look for the
249    * XV_IMAGE encoding so we can determine the maximum width and height
250    * supported */
251   XvQueryEncodings (context->disp, context->xv_port_id, &nb_encodings,
252       &encodings);
253
254   for (i = 0; i < nb_encodings; i++) {
255     GST_LOG ("Encoding %d, name %s, max wxh %lux%lu rate %d/%d",
256         i, encodings[i].name, encodings[i].width, encodings[i].height,
257         encodings[i].rate.numerator, encodings[i].rate.denominator);
258     if (strcmp (encodings[i].name, "XV_IMAGE") == 0) {
259       max_w = encodings[i].width;
260       max_h = encodings[i].height;
261     }
262   }
263
264   XvFreeEncodingInfo (encodings);
265
266   /* We get all image formats supported by our port */
267   formats = XvListImageFormats (context->disp,
268       context->xv_port_id, &nb_formats);
269   caps = gst_caps_new_empty ();
270   for (i = 0; i < nb_formats; i++) {
271     GstCaps *format_caps = NULL;
272     gboolean is_rgb_format = FALSE;
273     GstVideoFormat vformat;
274
275     /* We set the image format of the context to an existing one. This
276        is just some valid image format for making our xshm calls check before
277        caps negotiation really happens. */
278     context->im_format = formats[i].id;
279
280     switch (formats[i].type) {
281       case XvRGB:
282       {
283         XvImageFormatValues *fmt = &(formats[i]);
284         gint endianness;
285
286         endianness =
287             (fmt->byte_order == LSBFirst ? G_LITTLE_ENDIAN : G_BIG_ENDIAN);
288
289         vformat = gst_video_format_from_masks (fmt->depth, fmt->bits_per_pixel,
290             endianness, fmt->red_mask, fmt->green_mask, fmt->blue_mask, 0);
291         if (vformat == GST_VIDEO_FORMAT_UNKNOWN)
292           break;
293
294         format_caps = gst_caps_new_simple ("video/x-raw",
295             "format", G_TYPE_STRING, gst_video_format_to_string (vformat),
296             "width", GST_TYPE_INT_RANGE, 1, max_w,
297             "height", GST_TYPE_INT_RANGE, 1, max_h,
298             "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1, NULL);
299
300         is_rgb_format = TRUE;
301         break;
302       }
303       case XvYUV:
304       {
305         vformat = gst_video_format_from_fourcc (formats[i].id);
306         if (vformat == GST_VIDEO_FORMAT_UNKNOWN)
307           break;
308
309         format_caps = gst_caps_new_simple ("video/x-raw",
310             "format", G_TYPE_STRING, gst_video_format_to_string (vformat),
311             "width", GST_TYPE_INT_RANGE, 1, max_w,
312             "height", GST_TYPE_INT_RANGE, 1, max_h,
313             "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1, NULL);
314         break;
315       }
316       default:
317         vformat = GST_VIDEO_FORMAT_UNKNOWN;
318         g_assert_not_reached ();
319         break;
320     }
321
322     if (format_caps) {
323       GstXvImageFormat *format = NULL;
324
325       format = g_new0 (GstXvImageFormat, 1);
326       if (format) {
327         format->format = formats[i].id;
328         format->vformat = vformat;
329         format->caps = gst_caps_copy (format_caps);
330         context->formats_list = g_list_append (context->formats_list, format);
331       }
332
333       if (is_rgb_format) {
334         if (rgb_caps == NULL)
335           rgb_caps = format_caps;
336         else
337           gst_caps_append (rgb_caps, format_caps);
338       } else
339         gst_caps_append (caps, format_caps);
340     }
341   }
342
343   /* Collected all caps into either the caps or rgb_caps structures.
344    * Append rgb_caps on the end of YUV, so that YUV is always preferred */
345   if (rgb_caps)
346     gst_caps_append (caps, rgb_caps);
347
348   if (formats)
349     XFree (formats);
350
351   GST_DEBUG ("Generated the following caps: %" GST_PTR_FORMAT, caps);
352
353   if (gst_caps_is_empty (caps))
354     goto no_caps;
355
356   return caps;
357
358   /* ERRORS */
359 no_xv:
360   {
361     g_set_error (error, GST_RESOURCE_ERROR, GST_RESOURCE_ERROR_SETTINGS,
362         ("XVideo extension is not available"));
363     return NULL;
364   }
365 no_adaptors:
366   {
367     g_set_error (error, GST_RESOURCE_ERROR, GST_RESOURCE_ERROR_SETTINGS,
368         ("Failed getting XV adaptors list"));
369     return NULL;
370   }
371 no_ports:
372   {
373     context->adaptor_nr = -1;
374     g_set_error (error, GST_RESOURCE_ERROR, GST_RESOURCE_ERROR_BUSY,
375         ("No Xv Port available"));
376     return NULL;
377   }
378 no_caps:
379   {
380     gst_caps_unref (caps);
381     g_set_error (error, GST_STREAM_ERROR, GST_STREAM_ERROR_WRONG_TYPE,
382         ("No supported format found"));
383     return NULL;
384   }
385 }
386
387 /* This function calculates the pixel aspect ratio based on the properties
388  * in the context structure and stores it there. */
389 static void
390 gst_xvcontext_calculate_pixel_aspect_ratio (GstXvContext * context)
391 {
392   static const gint par[][2] = {
393     {1, 1},                     /* regular screen */
394     {16, 15},                   /* PAL TV */
395     {11, 10},                   /* 525 line Rec.601 video */
396     {54, 59},                   /* 625 line Rec.601 video */
397     {64, 45},                   /* 1280x1024 on 16:9 display */
398     {5, 3},                     /* 1280x1024 on 4:3 display */
399     {4, 3}                      /*  800x600 on 16:9 display */
400   };
401   gint i;
402   gint index;
403   gdouble ratio;
404   gdouble delta;
405
406 #define DELTA(idx) (ABS (ratio - ((gdouble) par[idx][0] / par[idx][1])))
407
408   /* first calculate the "real" ratio based on the X values;
409    * which is the "physical" w/h divided by the w/h in pixels of the display */
410   ratio = (gdouble) (context->widthmm * context->height)
411       / (context->heightmm * context->width);
412
413   /* DirectFB's X in 720x576 reports the physical dimensions wrong, so
414    * override here */
415   if (context->width == 720 && context->height == 576) {
416     ratio = 4.0 * 576 / (3.0 * 720);
417   }
418   GST_DEBUG ("calculated pixel aspect ratio: %f", ratio);
419
420   /* now find the one from par[][2] with the lowest delta to the real one */
421   delta = DELTA (0);
422   index = 0;
423
424   for (i = 1; i < sizeof (par) / (sizeof (gint) * 2); ++i) {
425     gdouble this_delta = DELTA (i);
426
427     if (this_delta < delta) {
428       index = i;
429       delta = this_delta;
430     }
431   }
432
433   GST_DEBUG ("Decided on index %d (%d/%d)", index,
434       par[index][0], par[index][1]);
435
436   g_free (context->par);
437   context->par = g_new0 (GValue, 1);
438   g_value_init (context->par, GST_TYPE_FRACTION);
439   gst_value_set_fraction (context->par, par[index][0], par[index][1]);
440   GST_DEBUG ("set context PAR to %d/%d",
441       gst_value_get_fraction_numerator (context->par),
442       gst_value_get_fraction_denominator (context->par));
443 }
444
445 #ifdef HAVE_XSHM
446 /* X11 stuff */
447 static gboolean error_caught = FALSE;
448
449 static int
450 gst_xvimage_handle_xerror (Display * display, XErrorEvent * xevent)
451 {
452   char error_msg[1024];
453
454   XGetErrorText (display, xevent->error_code, error_msg, 1024);
455   GST_DEBUG ("xvimage triggered an XError. error: %s", error_msg);
456   error_caught = TRUE;
457   return 0;
458 }
459
460 /* This function checks that it is actually really possible to create an image
461    using XShm */
462 static gboolean
463 gst_xvcontext_check_xshm_calls (GstXvContext * context)
464 {
465   XvImage *xvimage;
466   XShmSegmentInfo SHMInfo;
467   size_t size;
468   int (*handler) (Display *, XErrorEvent *);
469   gboolean result = FALSE;
470   gboolean did_attach = FALSE;
471
472   g_return_val_if_fail (context != NULL, FALSE);
473
474   /* Sync to ensure any older errors are already processed */
475   XSync (context->disp, FALSE);
476
477   /* Set defaults so we don't free these later unnecessarily */
478   SHMInfo.shmaddr = ((void *) -1);
479   SHMInfo.shmid = -1;
480
481   /* Setting an error handler to catch failure */
482   error_caught = FALSE;
483   handler = XSetErrorHandler (gst_xvimage_handle_xerror);
484
485   /* Trying to create a 1x1 picture */
486   GST_DEBUG ("XvShmCreateImage of 1x1");
487   xvimage = XvShmCreateImage (context->disp, context->xv_port_id,
488       context->im_format, NULL, 1, 1, &SHMInfo);
489
490   /* Might cause an error, sync to ensure it is noticed */
491   XSync (context->disp, FALSE);
492   if (!xvimage || error_caught) {
493     GST_WARNING ("could not XvShmCreateImage a 1x1 image");
494     goto beach;
495   }
496   size = xvimage->data_size;
497   SHMInfo.shmid = shmget (IPC_PRIVATE, size, IPC_CREAT | 0777);
498   if (SHMInfo.shmid == -1) {
499     GST_WARNING ("could not get shared memory of %" G_GSIZE_FORMAT " bytes",
500         size);
501     goto beach;
502   }
503
504   SHMInfo.shmaddr = shmat (SHMInfo.shmid, NULL, 0);
505   if (SHMInfo.shmaddr == ((void *) -1)) {
506     GST_WARNING ("Failed to shmat: %s", g_strerror (errno));
507     /* Clean up the shared memory segment */
508     shmctl (SHMInfo.shmid, IPC_RMID, NULL);
509     goto beach;
510   }
511
512   xvimage->data = SHMInfo.shmaddr;
513   SHMInfo.readOnly = FALSE;
514
515   if (XShmAttach (context->disp, &SHMInfo) == 0) {
516     GST_WARNING ("Failed to XShmAttach");
517     /* Clean up the shared memory segment */
518     shmctl (SHMInfo.shmid, IPC_RMID, NULL);
519     goto beach;
520   }
521
522   /* Sync to ensure we see any errors we caused */
523   XSync (context->disp, FALSE);
524
525   /* Delete the shared memory segment as soon as everyone is attached.
526    * This way, it will be deleted as soon as we detach later, and not
527    * leaked if we crash. */
528   shmctl (SHMInfo.shmid, IPC_RMID, NULL);
529
530   if (!error_caught) {
531     GST_DEBUG ("XServer ShmAttached to 0x%x, id 0x%lx", SHMInfo.shmid,
532         SHMInfo.shmseg);
533
534     did_attach = TRUE;
535     /* store whether we succeeded in result */
536     result = TRUE;
537   } else {
538     GST_WARNING ("MIT-SHM extension check failed at XShmAttach. "
539         "Not using shared memory.");
540   }
541
542 beach:
543   /* Sync to ensure we swallow any errors we caused and reset error_caught */
544   XSync (context->disp, FALSE);
545
546   error_caught = FALSE;
547   XSetErrorHandler (handler);
548
549   if (did_attach) {
550     GST_DEBUG ("XServer ShmDetaching from 0x%x id 0x%lx",
551         SHMInfo.shmid, SHMInfo.shmseg);
552     XShmDetach (context->disp, &SHMInfo);
553     XSync (context->disp, FALSE);
554   }
555   if (SHMInfo.shmaddr != ((void *) -1))
556     shmdt (SHMInfo.shmaddr);
557   if (xvimage)
558     XFree (xvimage);
559   return result;
560 }
561 #endif /* HAVE_XSHM */
562
563 static GstXvContext *
564 gst_xvcontext_copy (GstXvContext * context)
565 {
566   return NULL;
567 }
568
569 static void
570 gst_xvcontext_free (GstXvContext * context)
571 {
572   GList *formats_list, *channels_list;
573   gint i = 0;
574
575   GST_LOG ("free %p", context);
576
577   formats_list = context->formats_list;
578
579   while (formats_list) {
580     GstXvImageFormat *format = formats_list->data;
581
582     gst_caps_unref (format->caps);
583     g_free (format);
584     formats_list = g_list_next (formats_list);
585   }
586
587   if (context->formats_list)
588     g_list_free (context->formats_list);
589
590   channels_list = context->channels_list;
591
592   while (channels_list) {
593     GstColorBalanceChannel *channel = channels_list->data;
594
595     g_object_unref (channel);
596     channels_list = g_list_next (channels_list);
597   }
598
599   if (context->channels_list)
600     g_list_free (context->channels_list);
601
602   if (context->caps)
603     gst_caps_unref (context->caps);
604   if (context->last_caps)
605     gst_caps_unref (context->last_caps);
606
607   for (i = 0; i < context->nb_adaptors; i++) {
608     g_free (context->adaptors[i]);
609   }
610
611   g_free (context->adaptors);
612
613   g_free (context->par);
614
615   GST_DEBUG ("Closing display and freeing X Context");
616
617   if (context->xv_port_id)
618     XvUngrabPort (context->disp, context->xv_port_id, 0);
619
620   if (context->disp)
621     XCloseDisplay (context->disp);
622
623   g_mutex_clear (&context->lock);
624
625   g_slice_free1 (sizeof (GstXvContext), context);
626 }
627
628
629 /* This function gets the X Display and global info about it. Everything is
630    stored in our object and will be cleaned when the object is disposed. Note
631    here that caps for supported format are generated without any window or
632    image creation */
633 GstXvContext *
634 gst_xvcontext_new (GstXvContextConfig * config, GError ** error)
635 {
636   GstXvContext *context = NULL;
637   XPixmapFormatValues *px_formats = NULL;
638   gint nb_formats = 0, i, j, N_attr;
639   XvAttribute *xv_attr;
640   Atom prop_atom;
641   const char *channels[4] = { "XV_HUE", "XV_SATURATION",
642     "XV_BRIGHTNESS", "XV_CONTRAST"
643   };
644   int opcode, event, err;
645   int major = XkbMajorVersion;
646   int minor = XkbMinorVersion;
647
648   g_return_val_if_fail (config != NULL, NULL);
649
650   context = g_slice_new0 (GstXvContext);
651
652   gst_mini_object_init (GST_MINI_OBJECT_CAST (context), 0,
653       gst_xvcontext_get_type (),
654       (GstMiniObjectCopyFunction) gst_xvcontext_copy,
655       (GstMiniObjectDisposeFunction) NULL,
656       (GstMiniObjectFreeFunction) gst_xvcontext_free);
657
658   g_mutex_init (&context->lock);
659   context->im_format = 0;
660   context->adaptor_nr = -1;
661
662   if (!(context->disp = XOpenDisplay (config->display_name)))
663     goto no_display;
664
665   context->screen = DefaultScreenOfDisplay (context->disp);
666   context->screen_num = DefaultScreen (context->disp);
667   context->visual = DefaultVisual (context->disp, context->screen_num);
668   context->root = DefaultRootWindow (context->disp);
669   context->white = XWhitePixel (context->disp, context->screen_num);
670   context->black = XBlackPixel (context->disp, context->screen_num);
671   context->depth = DefaultDepthOfScreen (context->screen);
672
673   context->width = DisplayWidth (context->disp, context->screen_num);
674   context->height = DisplayHeight (context->disp, context->screen_num);
675   context->widthmm = DisplayWidthMM (context->disp, context->screen_num);
676   context->heightmm = DisplayHeightMM (context->disp, context->screen_num);
677
678   GST_DEBUG ("X reports %dx%d pixels and %d mm x %d mm",
679       context->width, context->height, context->widthmm, context->heightmm);
680
681   gst_xvcontext_calculate_pixel_aspect_ratio (context);
682   /* We get supported pixmap formats at supported depth */
683   px_formats = XListPixmapFormats (context->disp, &nb_formats);
684
685   if (!px_formats)
686     goto no_pixel_formats;
687
688   /* We get bpp value corresponding to our running depth */
689   for (i = 0; i < nb_formats; i++) {
690     if (px_formats[i].depth == context->depth)
691       context->bpp = px_formats[i].bits_per_pixel;
692   }
693
694   XFree (px_formats);
695
696   context->endianness =
697       (ImageByteOrder (context->disp) ==
698       LSBFirst) ? G_LITTLE_ENDIAN : G_BIG_ENDIAN;
699
700   /* our caps system handles 24/32bpp RGB as big-endian. */
701   if ((context->bpp == 24 || context->bpp == 32) &&
702       context->endianness == G_LITTLE_ENDIAN) {
703     context->endianness = G_BIG_ENDIAN;
704     context->visual->red_mask = GUINT32_TO_BE (context->visual->red_mask);
705     context->visual->green_mask = GUINT32_TO_BE (context->visual->green_mask);
706     context->visual->blue_mask = GUINT32_TO_BE (context->visual->blue_mask);
707     if (context->bpp == 24) {
708       context->visual->red_mask >>= 8;
709       context->visual->green_mask >>= 8;
710       context->visual->blue_mask >>= 8;
711     }
712   }
713
714   if (!(context->caps = gst_xvcontext_get_xv_support (context, config, error)))
715     goto no_caps;
716
717   /* Search for XShm extension support */
718 #ifdef HAVE_XSHM
719   if (XShmQueryExtension (context->disp) &&
720       gst_xvcontext_check_xshm_calls (context)) {
721     context->use_xshm = TRUE;
722     GST_DEBUG ("xvimagesink is using XShm extension");
723   } else
724 #endif /* HAVE_XSHM */
725   {
726     context->use_xshm = FALSE;
727     GST_DEBUG ("xvimagesink is not using XShm extension");
728   }
729   if (XkbQueryExtension (context->disp, &opcode, &event, &err, &major, &minor)) {
730     context->use_xkb = TRUE;
731     GST_DEBUG ("xvimagesink is using Xkb extension");
732   } else {
733     context->use_xkb = FALSE;
734     GST_DEBUG ("xvimagesink is not using Xkb extension");
735   }
736
737   xv_attr = XvQueryPortAttributes (context->disp, context->xv_port_id, &N_attr);
738
739   /* Generate the channels list */
740   for (i = 0; i < (sizeof (channels) / sizeof (char *)); i++) {
741     XvAttribute *matching_attr = NULL;
742
743     /* Retrieve the property atom if it exists. If it doesn't exist,
744      * the attribute itself must not either, so we can skip */
745     prop_atom = XInternAtom (context->disp, channels[i], True);
746     if (prop_atom == None)
747       continue;
748
749     if (xv_attr != NULL) {
750       for (j = 0; j < N_attr && matching_attr == NULL; ++j)
751         if (!g_ascii_strcasecmp (channels[i], xv_attr[j].name))
752           matching_attr = xv_attr + j;
753     }
754
755     if (matching_attr) {
756       GstColorBalanceChannel *channel;
757
758       channel = g_object_new (GST_TYPE_COLOR_BALANCE_CHANNEL, NULL);
759       channel->label = g_strdup (channels[i]);
760       channel->min_value = matching_attr->min_value;
761       channel->max_value = matching_attr->max_value;
762
763       context->channels_list = g_list_append (context->channels_list, channel);
764
765       /* If the colorbalance settings have not been touched we get Xv values
766          as defaults and update our internal variables */
767       if (!config->cb_changed) {
768         gint val;
769
770         XvGetPortAttribute (context->disp, context->xv_port_id,
771             prop_atom, &val);
772         /* Normalize val to [-1000, 1000] */
773         val = floor (0.5 + -1000 + 2000 * (val - channel->min_value) /
774             (double) (channel->max_value - channel->min_value));
775
776         if (!g_ascii_strcasecmp (channels[i], "XV_HUE"))
777           config->hue = val;
778         else if (!g_ascii_strcasecmp (channels[i], "XV_SATURATION"))
779           config->saturation = val;
780         else if (!g_ascii_strcasecmp (channels[i], "XV_BRIGHTNESS"))
781           config->brightness = val;
782         else if (!g_ascii_strcasecmp (channels[i], "XV_CONTRAST"))
783           config->contrast = val;
784       }
785     }
786   }
787
788   if (xv_attr)
789     XFree (xv_attr);
790
791   return context;
792
793   /* ERRORS */
794 no_display:
795   {
796     gst_xvcontext_unref (context);
797     g_set_error (error, GST_RESOURCE_ERROR, GST_RESOURCE_ERROR_WRITE,
798         "Could not open display %s", config->display_name);
799     return NULL;
800   }
801 no_pixel_formats:
802   {
803     gst_xvcontext_unref (context);
804     g_set_error (error, GST_RESOURCE_ERROR, GST_RESOURCE_ERROR_SETTINGS,
805         ("Could not get pixel formats"));
806     return NULL;
807   }
808 no_caps:
809   {
810     gst_xvcontext_unref (context);
811     return NULL;
812   }
813 }
814
815 void
816 gst_xvcontext_set_synchronous (GstXvContext * context, gboolean synchronous)
817 {
818   /* call XSynchronize with the current value of synchronous */
819   GST_DEBUG ("XSynchronize called with %s", synchronous ? "TRUE" : "FALSE");
820   g_mutex_lock (&context->lock);
821   XSynchronize (context->disp, synchronous);
822   g_mutex_unlock (&context->lock);
823 }
824
825 void
826 gst_xvcontext_update_colorbalance (GstXvContext * context,
827     GstXvContextConfig * config)
828 {
829   GList *channels = NULL;
830
831   /* Don't set the attributes if they haven't been changed, to avoid
832    * rounding errors changing the values */
833   if (!config->cb_changed)
834     return;
835
836   /* For each channel of the colorbalance we calculate the correct value
837      doing range conversion and then set the Xv port attribute to match our
838      values. */
839   channels = context->channels_list;
840
841   while (channels) {
842     if (channels->data && GST_IS_COLOR_BALANCE_CHANNEL (channels->data)) {
843       GstColorBalanceChannel *channel = NULL;
844       Atom prop_atom;
845       gint value = 0;
846       gdouble convert_coef;
847
848       channel = GST_COLOR_BALANCE_CHANNEL (channels->data);
849       g_object_ref (channel);
850
851       /* Our range conversion coef */
852       convert_coef = (channel->max_value - channel->min_value) / 2000.0;
853
854       if (g_ascii_strcasecmp (channel->label, "XV_HUE") == 0) {
855         value = config->hue;
856       } else if (g_ascii_strcasecmp (channel->label, "XV_SATURATION") == 0) {
857         value = config->saturation;
858       } else if (g_ascii_strcasecmp (channel->label, "XV_CONTRAST") == 0) {
859         value = config->contrast;
860       } else if (g_ascii_strcasecmp (channel->label, "XV_BRIGHTNESS") == 0) {
861         value = config->brightness;
862       } else {
863         g_warning ("got an unknown channel %s", channel->label);
864         g_object_unref (channel);
865         return;
866       }
867
868       /* Committing to Xv port */
869       g_mutex_lock (&context->lock);
870       prop_atom = XInternAtom (context->disp, channel->label, True);
871       if (prop_atom != None) {
872         int xv_value;
873         xv_value =
874             floor (0.5 + (value + 1000) * convert_coef + channel->min_value);
875         XvSetPortAttribute (context->disp,
876             context->xv_port_id, prop_atom, xv_value);
877       }
878       g_mutex_unlock (&context->lock);
879
880       g_object_unref (channel);
881     }
882     channels = g_list_next (channels);
883   }
884 }
885
886 /* This function tries to get a format matching with a given caps in the
887    supported list of formats we generated in gst_xvimagesink_get_xv_support */
888 gint
889 gst_xvcontext_get_format_from_info (GstXvContext * context,
890     const GstVideoInfo * info)
891 {
892   GList *list = NULL;
893
894   list = context->formats_list;
895
896   while (list) {
897     GstXvImageFormat *format = list->data;
898
899     if (format && format->vformat == GST_VIDEO_INFO_FORMAT (info))
900       return format->format;
901
902     list = g_list_next (list);
903   }
904   return -1;
905 }
906
907 void
908 gst_xvcontext_set_colorimetry (GstXvContext * context,
909     GstVideoColorimetry * colorimetry)
910 {
911   Atom prop_atom;
912   int xv_value;
913
914   if (!context->have_iturbt709 && !context->have_xvcolorspace)
915     return;
916
917   switch (colorimetry->matrix) {
918     case GST_VIDEO_COLOR_MATRIX_SMPTE240M:
919     case GST_VIDEO_COLOR_MATRIX_BT709:
920       xv_value = 1;
921       break;
922     default:
923       xv_value = 0;
924       break;
925   }
926
927   g_mutex_lock (&context->lock);
928   if (context->have_iturbt709) {
929     prop_atom = XInternAtom (context->disp, "XV_ITURBT_709", True);
930     if (prop_atom != None) {
931       XvSetPortAttribute (context->disp,
932           context->xv_port_id, prop_atom, xv_value);
933     }
934   }
935
936   if (context->have_xvcolorspace) {
937     prop_atom = XInternAtom (context->disp, "XV_COLORSPACE", True);
938     if (prop_atom != None) {
939       XvSetPortAttribute (context->disp,
940           context->xv_port_id, prop_atom, xv_value);
941     }
942   }
943   g_mutex_unlock (&context->lock);
944 }
945
946 GstXWindow *
947 gst_xvcontext_create_xwindow (GstXvContext * context, gint width, gint height)
948 {
949   GstXWindow *window;
950   Atom wm_delete;
951   Atom hints_atom = None;
952
953   g_return_val_if_fail (GST_IS_XVCONTEXT (context), NULL);
954
955   window = g_slice_new0 (GstXWindow);
956
957   window->context = gst_xvcontext_ref (context);
958   window->render_rect.x = window->render_rect.y = 0;
959   window->render_rect.w = width;
960   window->render_rect.h = height;
961   window->have_render_rect = FALSE;
962
963   window->width = width;
964   window->height = height;
965   window->internal = TRUE;
966
967   g_mutex_lock (&context->lock);
968
969   window->win = XCreateSimpleWindow (context->disp,
970       context->root, 0, 0, width, height, 0, 0, context->black);
971
972   /* We have to do that to prevent X from redrawing the background on
973    * ConfigureNotify. This takes away flickering of video when resizing. */
974   XSetWindowBackgroundPixmap (context->disp, window->win, None);
975
976   /* Tell the window manager we'd like delete client messages instead of
977    * being killed */
978   wm_delete = XInternAtom (context->disp, "WM_DELETE_WINDOW", True);
979   if (wm_delete != None) {
980     (void) XSetWMProtocols (context->disp, window->win, &wm_delete, 1);
981   }
982
983   hints_atom = XInternAtom (context->disp, "_MOTIF_WM_HINTS", True);
984   if (hints_atom != None) {
985     MotifWmHints *hints;
986
987     hints = g_malloc0 (sizeof (MotifWmHints));
988
989     hints->flags |= MWM_HINTS_DECORATIONS;
990     hints->decorations = 1 << 0;
991
992     XChangeProperty (context->disp, window->win,
993         hints_atom, hints_atom, 32, PropModeReplace,
994         (guchar *) hints, sizeof (MotifWmHints) / sizeof (long));
995
996     XSync (context->disp, FALSE);
997
998     g_free (hints);
999   }
1000
1001   window->gc = XCreateGC (context->disp, window->win, 0, NULL);
1002
1003   XMapRaised (context->disp, window->win);
1004
1005   XSync (context->disp, FALSE);
1006
1007   g_mutex_unlock (&context->lock);
1008
1009   return window;
1010 }
1011
1012 GstXWindow *
1013 gst_xvcontext_create_xwindow_from_xid (GstXvContext * context, XID xid)
1014 {
1015   GstXWindow *window;
1016   XWindowAttributes attr;
1017
1018   window = g_slice_new0 (GstXWindow);
1019   window->win = xid;
1020   window->context = gst_xvcontext_ref (context);
1021
1022   /* Set the event we want to receive and create a GC */
1023   g_mutex_lock (&context->lock);
1024
1025   XGetWindowAttributes (context->disp, window->win, &attr);
1026
1027   window->width = attr.width;
1028   window->height = attr.height;
1029   window->internal = FALSE;
1030
1031   window->have_render_rect = FALSE;
1032   window->render_rect.x = window->render_rect.y = 0;
1033   window->render_rect.w = attr.width;
1034   window->render_rect.h = attr.height;
1035
1036   window->gc = XCreateGC (context->disp, window->win, 0, NULL);
1037   g_mutex_unlock (&context->lock);
1038
1039   return window;
1040 }
1041
1042 void
1043 gst_xwindow_destroy (GstXWindow * window)
1044 {
1045   GstXvContext *context;
1046
1047   g_return_if_fail (window != NULL);
1048
1049   context = window->context;
1050
1051   g_mutex_lock (&context->lock);
1052
1053   /* If we did not create that window we just free the GC and let it live */
1054   if (window->internal)
1055     XDestroyWindow (context->disp, window->win);
1056   else
1057     XSelectInput (context->disp, window->win, 0);
1058
1059   XFreeGC (context->disp, window->gc);
1060
1061   XSync (context->disp, FALSE);
1062
1063   g_mutex_unlock (&context->lock);
1064
1065   gst_xvcontext_unref (context);
1066
1067   g_slice_free1 (sizeof (GstXWindow), window);
1068 }
1069
1070 void
1071 gst_xwindow_set_event_handling (GstXWindow * window, gboolean handle_events)
1072 {
1073   GstXvContext *context;
1074
1075   g_return_if_fail (window != NULL);
1076
1077   context = window->context;
1078
1079   g_mutex_lock (&context->lock);
1080   if (handle_events) {
1081     if (window->internal) {
1082       XSelectInput (context->disp, window->win,
1083           ExposureMask | StructureNotifyMask | PointerMotionMask |
1084           KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask);
1085     } else {
1086       XSelectInput (context->disp, window->win,
1087           ExposureMask | StructureNotifyMask | PointerMotionMask |
1088           KeyPressMask | KeyReleaseMask);
1089     }
1090   } else {
1091     XSelectInput (context->disp, window->win, 0);
1092   }
1093   g_mutex_unlock (&context->lock);
1094 }
1095
1096 void
1097 gst_xwindow_set_title (GstXWindow * window, const gchar * title)
1098 {
1099   GstXvContext *context;
1100
1101   g_return_if_fail (window != NULL);
1102
1103   context = window->context;
1104
1105   /* we have a window */
1106   if (window->internal && title) {
1107     XTextProperty xproperty;
1108     XClassHint *hint = XAllocClassHint ();
1109     Atom _NET_WM_NAME = XInternAtom (context->disp, "_NET_WM_NAME", 0);
1110     Atom UTF8_STRING = XInternAtom (context->disp, "UTF8_STRING", 0);
1111
1112     if ((XStringListToTextProperty (((char **) &title), 1, &xproperty)) != 0) {
1113       XSetWMName (context->disp, window->win, &xproperty);
1114       XFree (xproperty.value);
1115
1116       XChangeProperty (context->disp, window->win, _NET_WM_NAME, UTF8_STRING, 8,
1117           0, (unsigned char *) title, strlen (title));
1118       XSync (context->disp, False);
1119
1120       if (hint) {
1121         hint->res_name = (char *) title;
1122         hint->res_class = (char *) "GStreamer";
1123         XSetClassHint (context->disp, window->win, hint);
1124       }
1125       XFree (hint);
1126     }
1127   }
1128 }
1129
1130 void
1131 gst_xwindow_update_geometry (GstXWindow * window)
1132 {
1133   XWindowAttributes attr;
1134   GstXvContext *context;
1135
1136   g_return_if_fail (window != NULL);
1137
1138   context = window->context;
1139
1140   /* Update the window geometry */
1141   g_mutex_lock (&context->lock);
1142   XGetWindowAttributes (context->disp, window->win, &attr);
1143
1144   window->width = attr.width;
1145   window->height = attr.height;
1146
1147   if (!window->have_render_rect) {
1148     window->render_rect.x = window->render_rect.y = 0;
1149     window->render_rect.w = attr.width;
1150     window->render_rect.h = attr.height;
1151   }
1152
1153   g_mutex_unlock (&context->lock);
1154 }
1155
1156
1157 void
1158 gst_xwindow_clear (GstXWindow * window)
1159 {
1160   GstXvContext *context;
1161
1162   g_return_if_fail (window != NULL);
1163
1164   context = window->context;
1165
1166   g_mutex_lock (&context->lock);
1167
1168   XvStopVideo (context->disp, context->xv_port_id, window->win);
1169
1170   XSync (context->disp, FALSE);
1171
1172   g_mutex_unlock (&context->lock);
1173 }
1174
1175 void
1176 gst_xwindow_set_render_rectangle (GstXWindow * window,
1177     gint x, gint y, gint width, gint height)
1178 {
1179   g_return_if_fail (window != NULL);
1180
1181   if (width >= 0 && height >= 0) {
1182     window->render_rect.x = x;
1183     window->render_rect.y = y;
1184     window->render_rect.w = width;
1185     window->render_rect.h = height;
1186     window->have_render_rect = TRUE;
1187   } else {
1188     window->render_rect.x = 0;
1189     window->render_rect.y = 0;
1190     window->render_rect.w = window->width;
1191     window->render_rect.h = window->height;
1192     window->have_render_rect = FALSE;
1193   }
1194 }