]> git.sesse.net Git - vlc/blob - plugins/x11/vout_xvideo.c
7933f4cf5895a34f54617c113ec47521e7e4055c
[vlc] / plugins / x11 / vout_xvideo.c
1 /*****************************************************************************
2  * vout_xvideo.c: Xvideo video output display method
3  *****************************************************************************
4  * Copyright (C) 1998, 1999, 2000, 2001 VideoLAN
5  * $Id: vout_xvideo.c,v 1.12 2001/04/27 19:29:11 massiot Exp $
6  *
7  * Authors: Shane Harper <shanegh@optusnet.com.au>
8  *          Vincent Seguin <seguin@via.ecp.fr>
9  *          Samuel Hocevar <sam@zoy.org>
10  *          David Kennedy <dkennedy@tinytoad.com>
11  *
12  * This program is free software; you can redistribute it and/or modify
13  * it under the terms of the GNU General Public License as published by
14  * the Free Software Foundation; either version 2 of the License, or
15  * (at your option) any later version.
16  *
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with this program; if not, write to the Free Software
24  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
25  *****************************************************************************/
26
27 #define MODULE_NAME xvideo
28 #include "modules_inner.h"
29
30 /*****************************************************************************
31  * Preamble
32  *****************************************************************************/
33 #include "defs.h"
34
35 #include <errno.h>                                                 /* ENOMEM */
36 #include <stdlib.h>                                                /* free() */
37 #include <string.h>                                            /* strerror() */
38
39 #ifdef HAVE_MACHINE_PARAM_H
40 /* BSD */
41 #include <machine/param.h>
42 #include <sys/types.h>                                     /* typedef ushort */
43 #include <sys/ipc.h>
44 #endif
45
46 #include <sys/shm.h>                                   /* shmget(), shmctl() */
47 #include <X11/Xlib.h>
48 #include <X11/Xutil.h>
49 #include <X11/keysym.h>
50 #include <X11/extensions/XShm.h>
51 #include <X11/extensions/Xv.h>
52 #include <X11/extensions/Xvlib.h>
53
54 #include "config.h"
55 #include "common.h"
56 #include "threads.h"
57 #include "mtime.h"
58 #include "tests.h"
59 #include "modules.h"
60
61 #include "video.h"
62 #include "video_output.h"
63
64 #include "interface.h"
65 #include "intf_msg.h"
66
67 #include "netutils.h"                                 /* network_ChannelJoin */
68
69 #include "main.h"
70
71 #include "stream_control.h"                 /* needed by input_ext-intf.h... */
72 #include "input_ext-intf.h"
73
74
75 #define GUID_YUV12_PLANAR 0x32315659
76
77
78 /*****************************************************************************
79  * vout_sys_t: video output X11 method descriptor
80  *****************************************************************************
81  * This structure is part of the video output thread descriptor.
82  * It describes the XVideo specific properties of an output thread.
83  *****************************************************************************/
84 typedef struct vout_sys_s
85 {
86     /* User settings */
87 #if 0
88     /* this plugin (currently) requires the SHM Ext... */
89     boolean_t           b_shm;               /* shared memory extension flag */
90 #endif
91
92     /* Internal settings and properties */
93     Display *           p_display;                        /* display pointer */
94     int                 i_screen;                           /* screen number */
95     Window              window;                               /* root window */
96     GC                  gc;              /* graphic context instance handler */
97     Window              yuv_window;   /* sub-window for displaying yuv video
98                                                                         data */
99     GC                  yuv_gc;
100     int                 xv_port;
101
102     /* Display buffers and shared memory information */
103     /* Note: only 1 buffer... Xv ext does double buffering. */
104     XvImage *           p_xvimage;
105     int                 i_image_width;
106     int                 i_image_height;
107                                 /* i_image_width & i_image_height reflect the
108                                  * size of the XvImage. They are used by
109                                  * vout_Display() to check if the image to be
110                                  * displayed can use the current XvImage. */
111     XShmSegmentInfo     shm_info;       /* shared memory zone information */
112
113     /* X11 generic properties */
114     Atom                wm_protocols;
115     Atom                wm_delete_window;
116
117     int                 i_window_width;              /* width of main window */
118     int                 i_window_height;            /* height of main window */
119
120
121     /* Screen saver properties */
122     int                 i_ss_timeout;                             /* timeout */
123     int                 i_ss_interval;           /* interval between changes */
124     int                 i_ss_blanking;                      /* blanking mode */
125     int                 i_ss_exposure;                      /* exposure mode */
126     
127     /* Mouse pointer properties */
128     boolean_t           b_mouse_pointer_visible;
129     mtime_t             i_time_mouse_last_moved; /* used to auto-hide pointer*/
130
131     /* Displaying fullscreen */
132     boolean_t           b_fullscreen;
133
134 } vout_sys_t;
135
136 /* Fullscreen needs to be able to hide the wm decorations */
137 #define MWM_HINTS_DECORATIONS   (1L << 1)
138 #define PROP_MWM_HINTS_ELEMENTS 5
139 typedef struct mwmhints_s
140 {
141     u32 flags;
142     u32 functions;
143     u32 decorations;
144     s32 input_mode;
145     u32 status;
146 } mwmhints_t;
147
148 /*****************************************************************************
149  * Local prototypes
150  *****************************************************************************/
151 static int  vout_Probe     ( probedata_t * );
152 static int  vout_Create    ( vout_thread_t * );
153 static int  vout_Init      ( vout_thread_t * );
154 static void vout_End       ( vout_thread_t * );
155 static void vout_Destroy   ( vout_thread_t * );
156 static int  vout_Manage    ( vout_thread_t * );
157 static void vout_Display   ( vout_thread_t * );
158 static void vout_SetPalette( vout_thread_t *, u16 *, u16 *, u16 *, u16 * );
159
160 static int  XVideoCreateWindow       ( vout_thread_t * );
161 static void XVideoDestroyWindow      ( vout_thread_t *p_vout );
162 static int  XVideoUpdateImgSizeIfRequired( vout_thread_t *p_vout );
163 static int  XVideoCreateShmImage     ( Display* dpy, int xv_port,
164                                        XvImage **pp_xvimage,
165                                        XShmSegmentInfo *p_shm_info,
166                                        int i_width, int i_height );
167 static void XVideoDestroyShmImage    ( vout_thread_t *, XvImage *,
168                                        XShmSegmentInfo * );
169 static void XVideoSetMousePointer    ( const vout_thread_t * );
170 static void XVideoEnableScreenSaver  ( vout_thread_t * );
171 static void XVideoDisableScreenSaver ( vout_thread_t * );
172 /*static void XVideoSetAttribute       ( vout_thread_t *, char *, float );*/
173
174 static int  XVideoCheckForXv         ( Display * );
175 static int  XVideoGetPort            ( Display * );
176 static void XVideoOutputCoords       ( const picture_t *, const boolean_t,
177                                        const int, const int,
178                                        int *, int *, int *, int * );
179 static void XVideoDisplay            ( vout_thread_t * );
180
181 /*****************************************************************************
182  * Functions exported as capabilities. They are declared as static so that
183  * we don't pollute the namespace too much.
184  *****************************************************************************/
185 void _M( vout_getfunctions )( function_list_t * p_function_list )
186 {
187     p_function_list->pf_probe = vout_Probe;
188     p_function_list->functions.vout.pf_create     = vout_Create;
189     p_function_list->functions.vout.pf_init       = vout_Init;
190     p_function_list->functions.vout.pf_end        = vout_End;
191     p_function_list->functions.vout.pf_destroy    = vout_Destroy;
192     p_function_list->functions.vout.pf_manage     = vout_Manage;
193     p_function_list->functions.vout.pf_display    = vout_Display;
194     p_function_list->functions.vout.pf_setpalette = vout_SetPalette;
195 }
196
197 /*****************************************************************************
198  * vout_Probe: probe the video driver and return a score
199  *****************************************************************************
200  * This returns a score to the plugin manager so that it can select the best
201  * plugin.
202  *****************************************************************************/
203 static int vout_Probe( probedata_t *p_data )
204 {
205     if( TestMethod( VOUT_METHOD_VAR, "xvideo" ) )
206     {
207         return( 999 );
208     }
209
210     return( 110 );
211 }
212
213 /*****************************************************************************
214  * vout_Create: allocate XVideo video thread output method
215  *****************************************************************************
216  * This function allocate and initialize a XVideo vout method. It uses some of
217  * the vout properties to choose the window size, and change them according to
218  * the actual properties of the display.
219  *****************************************************************************/
220 static int vout_Create( vout_thread_t *p_vout )
221 {
222     char *psz_display;
223
224     /* Allocate structure */
225     p_vout->p_sys = malloc( sizeof( vout_sys_t ) );
226     if( p_vout->p_sys == NULL )
227     {
228         intf_ErrMsg( "vout error: %s", strerror(ENOMEM) );
229         return( 1 );
230     }
231
232     /* Open display, unsing 'vlc_display' or DISPLAY environment variable */
233     psz_display = XDisplayName( main_GetPszVariable( VOUT_DISPLAY_VAR, NULL ) );
234     p_vout->p_sys->p_display = XOpenDisplay( psz_display );
235
236     if( p_vout->p_sys->p_display == NULL )                          /* error */
237     {
238         intf_ErrMsg( "vout error: cannot open display %s", psz_display );
239         free( p_vout->p_sys );
240         return( 1 );
241     }
242     p_vout->p_sys->i_screen = DefaultScreen( p_vout->p_sys->p_display );
243
244     p_vout->p_sys->b_fullscreen
245         = main_GetIntVariable( VOUT_FULLSCREEN_VAR, VOUT_FULLSCREEN_DEFAULT );
246     
247     if( !XVideoCheckForXv( p_vout->p_sys->p_display ) )
248     {
249         intf_ErrMsg( "vout error: no XVideo extension" );
250         XCloseDisplay( p_vout->p_sys->p_display );
251         free( p_vout->p_sys );
252         return( 1 );
253     }
254
255     /* Spawn base window - this window will include the video output window,
256      * but also command buttons, subtitles and other indicators */
257     if( XVideoCreateWindow( p_vout ) )
258     {
259         intf_ErrMsg( "vout error: cannot create XVideo window" );
260         XCloseDisplay( p_vout->p_sys->p_display );
261         free( p_vout->p_sys );
262         return( 1 );
263     }
264
265     if( (p_vout->p_sys->xv_port = XVideoGetPort( p_vout->p_sys->p_display ))<0 )
266         return 1;
267     intf_DbgMsg( "Using xv port %d" , p_vout->p_sys->xv_port );
268
269 #if 0
270     /* XXX The brightness and contrast values should be read from environment
271      * XXX variables... */
272     XVideoSetAttribute( p_vout, "XV_BRIGHTNESS", 0.5 );
273     XVideoSetAttribute( p_vout, "XV_CONTRAST",   0.5 );
274 #endif
275
276     p_vout->p_sys->b_mouse_pointer_visible = 1;
277
278     /* Disable screen saver and return */
279     XVideoDisableScreenSaver( p_vout );
280
281     return( 0 );
282 }
283
284 /*****************************************************************************
285  * vout_Init: initialize XVideo video thread output method
286  *****************************************************************************/
287 static int vout_Init( vout_thread_t *p_vout )
288 {
289 #ifdef SYS_DARWIN1_3
290     /* FIXME : As of 2001-03-16, XFree4 for MacOS X does not support Xshm. */
291     p_vout->p_sys->b_shm = 0;
292 #endif
293     p_vout->b_need_render = 0;
294     p_vout->p_sys->i_image_width = p_vout->p_sys->i_image_height = 0;
295
296     return( 0 );
297 }
298
299 /*****************************************************************************
300  * vout_End: terminate XVideo video thread output method
301  *****************************************************************************
302  * Destroy the XvImage. It is called at the end of the thread, but also each
303  * time the image is resized.
304  *****************************************************************************/
305 static void vout_End( vout_thread_t *p_vout )
306 {
307     XVideoDestroyShmImage( p_vout, p_vout->p_sys->p_xvimage,
308                            &p_vout->p_sys->shm_info );
309 }
310
311 /*****************************************************************************
312  * vout_Destroy: destroy XVideo video thread output method
313  *****************************************************************************
314  * Terminate an output method created by vout_Create
315  *****************************************************************************/
316 static void vout_Destroy( vout_thread_t *p_vout )
317 {
318     XVideoEnableScreenSaver( p_vout );
319     XVideoDestroyWindow( p_vout );
320     XCloseDisplay( p_vout->p_sys->p_display );
321
322     /* Destroy structure */
323     free( p_vout->p_sys );
324 }
325
326 /*****************************************************************************
327  * vout_Manage: handle X11 events
328  *****************************************************************************
329  * This function should be called regularly by video output thread. It manages
330  * X11 events and allows window resizing. It returns a non null value on
331  * error.
332  *
333  * XXX  Should "factor-out" common code in this and the "same" fn in the x11
334  * XXX  plugin!
335  *****************************************************************************/
336 static int vout_Manage( vout_thread_t *p_vout )
337 {
338     XEvent      xevent;                                         /* X11 event */
339     boolean_t   b_toggle_fullscreen;               /* user wants full-screen */
340     char        i_key;                                    /* ISO Latin-1 key */
341     KeySym      x_key_symbol;
342
343     b_toggle_fullscreen = 0;
344
345     /* Handle X11 events: ConfigureNotify events are parsed to know if the
346      * output window's size changed, MapNotify and UnmapNotify to know if the
347      * window is mapped (and if the display is useful), and ClientMessages
348      * to intercept window destruction requests */
349     while( XCheckWindowEvent( p_vout->p_sys->p_display, p_vout->p_sys->window,
350                               StructureNotifyMask | KeyPressMask |
351                               ButtonPressMask | ButtonReleaseMask | 
352                               PointerMotionMask, &xevent )
353            == True )
354     {
355         /* ConfigureNotify event: prepare  */
356         if( (xevent.type == ConfigureNotify)
357             /*&& ((xevent.xconfigure.width != p_vout->p_sys->i_window_width)
358                 || (xevent.xconfigure.height != p_vout->p_sys->i_window_height))*/ )
359         {
360             /* Update dimensions */
361             p_vout->p_sys->i_window_width = xevent.xconfigure.width;
362             p_vout->p_sys->i_window_height = xevent.xconfigure.height;
363         }
364         /* MapNotify event: change window status and disable screen saver */
365         else if( xevent.type == MapNotify)
366         {
367             if( (p_vout != NULL) && !p_vout->b_active )
368             {
369                 XVideoDisableScreenSaver( p_vout );
370                 p_vout->b_active = 1;
371             }
372         }
373         /* UnmapNotify event: change window status and enable screen saver */
374         else if( xevent.type == UnmapNotify )
375         {
376             if( (p_vout != NULL) && p_vout->b_active )
377             {
378                 XVideoEnableScreenSaver( p_vout );
379                 p_vout->b_active = 0;
380             }
381         }
382         /* Keyboard event */
383         else if( xevent.type == KeyPress )
384         {
385             /* We may have keys like F1 trough F12, ESC ... */
386             x_key_symbol = XKeycodeToKeysym( p_vout->p_sys->p_display,
387                                              xevent.xkey.keycode, 0 );
388             switch( x_key_symbol )
389             {
390                  case XK_Escape:
391                      p_main->p_intf->b_die = 1;
392                      break;
393                  case XK_Menu:
394                      p_main->p_intf->b_menu_change = 1;
395                      break;
396                  default:
397                      /* "Normal Keys"
398                       * The reason why I use this instead of XK_0 is that 
399                       * with XLookupString, we don't have to care about
400                       * keymaps. */
401
402                     if( XLookupString( &xevent.xkey, &i_key, 1, NULL, NULL ) )
403                     {
404                         switch( i_key )
405                         {
406                         case 'q':
407                         case 'Q':
408                             p_main->p_intf->b_die = 1;
409                             break;
410                         case 'f':
411                         case 'F':
412                             b_toggle_fullscreen = 1;
413                             break;
414                         case '0':
415                             network_ChannelJoin( 0 );
416                             break;
417                         case '1':
418                             network_ChannelJoin( 1 );
419                             break;
420                         case '2':
421                             network_ChannelJoin( 2 );
422                             break;
423                         case '3':
424                             network_ChannelJoin( 3 );
425                             break;
426                         case '4':
427                             network_ChannelJoin( 4 );
428                             break;
429                         case '5':
430                             network_ChannelJoin( 5 );
431                             break;
432                         case '6':
433                             network_ChannelJoin( 6 );
434                             break;
435                         case '7':
436                             network_ChannelJoin( 7 );
437                             break;
438                         case '8':
439                             network_ChannelJoin( 8 );
440                             break;
441                         case '9':
442                             network_ChannelJoin( 9 );
443                             break;
444                         default:
445                             if( intf_ProcessKey( p_main->p_intf, 
446                                                  (char )i_key ) )
447                             {
448                                intf_DbgMsg( "unhandled key '%c' (%i)", 
449                                             (char)i_key, i_key );
450                             }
451                             break;
452                         }
453                     }
454                 break;
455             }
456         }
457         /* Mouse click */
458         else if( xevent.type == ButtonPress )
459         {
460             switch( ((XButtonEvent *)&xevent)->button )
461             {
462                 case Button1:
463                     /* in this part we will eventually manage
464                      * clicks for DVD navigation for instance */
465                     break;
466             }
467         }
468         /* Mouse release */
469         else if( xevent.type == ButtonRelease )
470         {
471             switch( ((XButtonEvent *)&xevent)->button )
472             {
473                 case Button3:
474                     /* FIXME: need locking ! */
475                     p_main->p_intf->b_menu_change = 1;
476                     break;
477             }
478         }
479         /* Mouse move */
480         else if( xevent.type == MotionNotify )
481         {
482             p_vout->p_sys->i_time_mouse_last_moved = mdate();
483             p_vout->p_sys->b_mouse_pointer_visible = 1; 
484             XVideoSetMousePointer( p_vout ); 
485         }
486         /* Other event */
487         else
488         {
489             intf_WarnMsg( 1, "%p -> unhandled event type %d received",
490                          p_vout, xevent.type );
491         }
492     }
493
494     /* Handle events for YUV video output sub-window */
495     while( XCheckWindowEvent( p_vout->p_sys->p_display,
496                               p_vout->p_sys->yuv_window,
497                               ExposureMask, &xevent ) == True )
498     {
499         /* Window exposed (only handled if stream playback is paused) */
500         if( xevent.type == Expose )
501         {
502             if( ((XExposeEvent *)&xevent)->count == 0 )
503                 /* (if this is the last a collection of expose events...) */
504                 if( p_main->p_intf->p_input )
505                     if( PAUSE_S ==
506                             p_main->p_intf->p_input->stream.control.i_status )
507                         XVideoDisplay( p_vout );
508         }
509     }
510         
511     /* ClientMessage event - only WM_PROTOCOLS with WM_DELETE_WINDOW data
512      * are handled - according to the man pages, the format is always 32
513      * in this case */
514     while( XCheckTypedEvent( p_vout->p_sys->p_display,
515                              ClientMessage, &xevent ) )
516     {
517         if( (xevent.xclient.message_type == p_vout->p_sys->wm_protocols)
518             && (xevent.xclient.data.l[0] == p_vout->p_sys->wm_delete_window ) )
519         {
520             p_main->p_intf->b_die = 1;
521         }
522         else
523         {
524             intf_DbgMsg( "%p -> unhandled ClientMessage received", p_vout );
525         }
526     }
527
528     if ( b_toggle_fullscreen )
529     {
530         intf_DbgMsg( "vout: changing full-screen status" );
531
532         p_vout->p_sys->b_fullscreen = !p_vout->p_sys->b_fullscreen;
533
534         /* Get rid of the old window */
535         XVideoDestroyWindow( p_vout );
536
537         /* And create a new one */
538         if( XVideoCreateWindow( p_vout ) )
539         {
540             intf_ErrMsg( "vout error: cannot create X11 window" );
541             XCloseDisplay( p_vout->p_sys->p_display );
542
543             free( p_vout->p_sys );
544             return( 1 );
545         }
546     }
547
548     
549     if( (p_vout->i_changes & VOUT_GRAYSCALE_CHANGE))
550     {
551         /* FIXME: clear flags ?? */
552     }
553
554     /*
555      * Size change
556      */
557     if( p_vout->i_changes & VOUT_SIZE_CHANGE )
558     {
559         intf_DbgMsg( "vout: resizing window" );
560         p_vout->i_changes &= ~VOUT_SIZE_CHANGE;
561         /* Nothing to do here...
562          * vout_Display() detects size changes of the image to be displayed and
563          * re-creates the XvImage.*/
564         intf_Msg( "vout: video display resized (%dx%d)",
565                   p_vout->i_width, p_vout->i_height );
566     }
567
568     /* Autohide Cursor */
569     if( p_vout->p_sys->b_mouse_pointer_visible &&
570         mdate() - p_vout->p_sys->i_time_mouse_last_moved > 2000000 )
571     {
572         p_vout->p_sys->b_mouse_pointer_visible = 0; 
573         XVideoSetMousePointer( p_vout );
574     }
575     
576     return 0;
577 }
578
579 /*****************************************************************************
580  * vout_Display: displays previously rendered output
581  *****************************************************************************
582  * This function sends the currently rendered image to X11 server.
583  * (The Xv extension takes care of "double-buffering".)
584  *****************************************************************************/
585 static void vout_Display( vout_thread_t *p_vout )
586 {
587     boolean_t b_draw = 1;
588     int i_size = p_vout->p_rendered_pic->i_width *
589                    p_vout->p_rendered_pic->i_height;
590
591     if( XVideoUpdateImgSizeIfRequired( p_vout ) )
592         return;
593
594     switch( p_vout->p_rendered_pic->i_type )
595     {
596     case YUV_422_PICTURE:
597         intf_ErrMsg( "vout error: YUV_422_PICTURE not (yet) supported" );
598         b_draw = 0;
599         break;
600
601     case YUV_444_PICTURE:
602         intf_ErrMsg( "vout error: YUV_444_PICTURE not (yet) supported" );
603         b_draw = 0;
604         break;
605
606     case YUV_420_PICTURE:
607         memcpy( p_vout->p_sys->p_xvimage->data,
608                 p_vout->p_rendered_pic->p_y, i_size );
609         memcpy( p_vout->p_sys->p_xvimage->data + ( i_size ),
610                 p_vout->p_rendered_pic->p_v, i_size / 4 );
611         memcpy( p_vout->p_sys->p_xvimage->data + ( i_size ) + ( i_size / 4 ),
612                 p_vout->p_rendered_pic->p_u, i_size / 4 );
613         break;
614     }
615
616     if( b_draw )
617         XVideoDisplay( p_vout );
618 }
619
620 static void vout_SetPalette( p_vout_thread_t p_vout,
621                              u16 *red, u16 *green, u16 *blue, u16 *transp )
622 {
623     return;
624 }
625
626 /* following functions are local */
627
628 /*****************************************************************************
629  * XVideoUpdateImgSizeIfRequired 
630  *****************************************************************************
631  * This function checks to see if the image to be displayed is of a different
632  * size to the last image displayed. If so, the old shm block must be
633  * destroyed and a new one created.
634  * Note: the "image size" is the size of the image to be passed to the Xv
635  * extension (which is probably different to the size of the output window).
636  *****************************************************************************/
637 static int XVideoUpdateImgSizeIfRequired( vout_thread_t *p_vout )
638 {
639     int i_img_width         = p_vout->p_rendered_pic->i_width;
640     int i_img_height        = p_vout->p_rendered_pic->i_height;
641
642     if( p_vout->p_sys->i_image_width != i_img_width
643             || p_vout->p_sys->i_image_height != i_img_height )
644     {
645         p_vout->p_sys->i_image_width  = i_img_width;
646         p_vout->p_sys->i_image_height = i_img_height;
647
648         /* Destroy XvImage to change its size */
649         vout_End( p_vout );
650             /* Note: vout_End does nothing if no XvImage to destroy. */
651
652         /* Create XvImage using XShm extension */
653         if( XVideoCreateShmImage( p_vout->p_sys->p_display,
654                                   p_vout->p_sys->xv_port,
655                                   &p_vout->p_sys->p_xvimage,
656                                   &p_vout->p_sys->shm_info,
657                                   i_img_width, i_img_height ) )
658         {
659             intf_ErrMsg( "vout: failed to create xvimage." );
660             p_vout->p_sys->i_image_width = 0;
661             return( 1 );
662         }
663
664         /* Set bytes per line and initialize buffers */
665         p_vout->i_bytes_per_line =
666             (p_vout->p_sys->p_xvimage->data_size) /
667             (p_vout->p_sys->p_xvimage->height);
668
669         /* vout_SetBuffers( p_vout, p_vout->p_sys->p_xvimage->data ); */
670     }
671
672     return( 0 );
673 }
674
675 /*****************************************************************************
676  * XVideoCheckForXv: check for the XVideo extension
677  *****************************************************************************/
678 static int XVideoCheckForXv( Display *dpy )
679 {
680     unsigned int i;
681
682     switch( XvQueryExtension( dpy, &i, &i, &i, &i, &i ) )
683     {
684         case Success:
685             return( 1 );
686
687         case XvBadExtension:
688             intf_ErrMsg( "vout error: XvBadExtension" );
689             return( 0 );
690
691         case XvBadAlloc:
692             intf_ErrMsg( "vout error: XvBadAlloc" );
693             return( 0 );
694
695         default:
696             intf_ErrMsg( "vout error: XvQueryExtension failed" );
697             return( 0 );
698     }
699 }
700
701 /*****************************************************************************
702  * XVideoCreateWindow: open and set-up XVideo main window
703  *****************************************************************************/
704 static int XVideoCreateWindow( vout_thread_t *p_vout )
705 {
706     XSizeHints              xsize_hints;
707     XSetWindowAttributes    xwindow_attributes;
708     XGCValues               xgcvalues;
709     XEvent                  xevent;
710     Atom                    prop;
711     mwmhints_t              mwmhints;
712     
713     boolean_t               b_expose;
714     boolean_t               b_configure_notify;
715     boolean_t               b_map_notify;
716
717
718     /* Set main window's size */
719     /* If we're full screen, we're full screen! */
720     if( p_vout->p_sys->b_fullscreen )
721     {
722         p_vout->p_sys->i_window_width = DisplayWidth( p_vout->p_sys->p_display,
723                                                       p_vout->p_sys->i_screen );
724         p_vout->p_sys->i_window_height =  DisplayHeight( p_vout->p_sys->p_display,
725                                                          p_vout->p_sys->i_screen );
726 /*      p_vout->i_width =  p_vout->p_sys->i_window_width;
727         p_vout->i_height = p_vout->p_sys->i_window_height; */
728     }
729     else
730     {
731         p_vout->p_sys->i_window_width =  main_GetIntVariable( VOUT_WIDTH_VAR,
732                                                        VOUT_WIDTH_DEFAULT );
733         p_vout->p_sys->i_window_height = main_GetIntVariable( VOUT_HEIGHT_VAR,
734                                                        VOUT_HEIGHT_DEFAULT );
735     }
736
737     /* Prepare window manager hints and properties */
738     xsize_hints.base_width          = p_vout->p_sys->i_window_width;
739     xsize_hints.base_height         = p_vout->p_sys->i_window_height;
740     xsize_hints.flags               = PSize;
741     p_vout->p_sys->wm_protocols     = XInternAtom( p_vout->p_sys->p_display,
742                                                    "WM_PROTOCOLS", True );
743     p_vout->p_sys->wm_delete_window = XInternAtom( p_vout->p_sys->p_display,
744                                                    "WM_DELETE_WINDOW", True );
745
746     /* Prepare window attributes */
747     xwindow_attributes.background_pixel = BlackPixel( p_vout->p_sys->p_display,
748                                                       p_vout->p_sys->i_screen );
749
750     xwindow_attributes.event_mask = ExposureMask | StructureNotifyMask;
751
752     /* Create the window and set hints - the window must receive ConfigureNotify
753      * events, and, until it is displayed, Expose and MapNotify events. */
754     p_vout->p_sys->window =
755             XCreateWindow( p_vout->p_sys->p_display,
756                            DefaultRootWindow( p_vout->p_sys->p_display ),
757                            0, 0,
758                            p_vout->p_sys->i_window_width,
759                            p_vout->p_sys->i_window_height, 1,
760                            0, InputOutput, 0,
761                            CWBackPixel | CWEventMask,
762                            &xwindow_attributes );
763
764     if ( p_vout->p_sys->b_fullscreen )
765     {
766         prop = XInternAtom(p_vout->p_sys->p_display, "_MOTIF_WM_HINTS", False);
767         mwmhints.flags = MWM_HINTS_DECORATIONS;
768         mwmhints.decorations = 0;
769         XChangeProperty( p_vout->p_sys->p_display, p_vout->p_sys->window,
770                          prop, prop, 32, PropModeReplace,
771                          (unsigned char *)&mwmhints, PROP_MWM_HINTS_ELEMENTS );
772
773         XSetTransientForHint( p_vout->p_sys->p_display,
774                               p_vout->p_sys->window, None );
775         XRaiseWindow( p_vout->p_sys->p_display, p_vout->p_sys->window );
776     }
777
778     
779     /* Set window manager hints and properties: size hints, command,
780      * window's name, and accepted protocols */
781     XSetWMNormalHints( p_vout->p_sys->p_display, p_vout->p_sys->window,
782                        &xsize_hints );
783     XSetCommand( p_vout->p_sys->p_display, p_vout->p_sys->window,
784                  p_main->ppsz_argv, p_main->i_argc );
785     XStoreName( p_vout->p_sys->p_display, p_vout->p_sys->window,
786                 VOUT_TITLE " (XVideo output)" );
787
788     if( (p_vout->p_sys->wm_protocols == None)        /* use WM_DELETE_WINDOW */
789         || (p_vout->p_sys->wm_delete_window == None)
790         || !XSetWMProtocols( p_vout->p_sys->p_display, p_vout->p_sys->window,
791                              &p_vout->p_sys->wm_delete_window, 1 ) )
792     {
793         /* WM_DELETE_WINDOW is not supported by window manager */
794         intf_Msg( "vout error: missing or bad window manager" );
795     }
796
797     /* Creation of a graphic context that doesn't generate a GraphicsExpose
798      * event when using functions like XCopyArea */
799     xgcvalues.graphics_exposures = False;
800     p_vout->p_sys->gc = XCreateGC( p_vout->p_sys->p_display,
801                                    p_vout->p_sys->window,
802                                    GCGraphicsExposures, &xgcvalues);
803
804     /* Send orders to server, and wait until window is displayed - three
805      * events must be received: a MapNotify event, an Expose event allowing
806      * drawing in the window, and a ConfigureNotify to get the window
807      * dimensions. Once those events have been received, only ConfigureNotify
808      * events need to be received. */
809     b_expose = 0;
810     b_configure_notify = 0;
811     b_map_notify = 0;
812     XMapWindow( p_vout->p_sys->p_display, p_vout->p_sys->window);
813     do
814     {
815         XNextEvent( p_vout->p_sys->p_display, &xevent);
816         if( (xevent.type == Expose)
817             && (xevent.xexpose.window == p_vout->p_sys->window) )
818         {
819             b_expose = 1;
820         }
821         else if( (xevent.type == MapNotify)
822                  && (xevent.xmap.window == p_vout->p_sys->window) )
823         {
824             b_map_notify = 1;
825         }
826         else if( (xevent.type == ConfigureNotify)
827                  && (xevent.xconfigure.window == p_vout->p_sys->window) )
828         {
829             b_configure_notify = 1;
830             p_vout->p_sys->i_window_width = xevent.xconfigure.width;
831             p_vout->p_sys->i_window_height = xevent.xconfigure.height;
832         }
833     } while( !( b_expose && b_configure_notify && b_map_notify ) );
834
835     XSelectInput( p_vout->p_sys->p_display, p_vout->p_sys->window,
836                   StructureNotifyMask | KeyPressMask |
837                   ButtonPressMask | ButtonReleaseMask | 
838                   PointerMotionMask );
839
840     if( p_vout->p_sys->b_fullscreen )
841     {
842         XSetInputFocus( p_vout->p_sys->p_display, p_vout->p_sys->window,
843                         RevertToNone, CurrentTime );
844         XMoveWindow( p_vout->p_sys->p_display, p_vout->p_sys->window, 0, 0 );
845     }
846
847     /* Create YUV output sub-window. */
848     p_vout->p_sys->yuv_window=XCreateSimpleWindow( p_vout->p_sys->p_display,
849                          p_vout->p_sys->window, 0, 0, 1, 1, 0,
850                          BlackPixel( p_vout->p_sys->p_display,
851                                          p_vout->p_sys->i_screen ),
852                          WhitePixel( p_vout->p_sys->p_display,
853                                          p_vout->p_sys->i_screen ) );
854  
855     p_vout->p_sys->yuv_gc = XCreateGC( p_vout->p_sys->p_display,
856                                         p_vout->p_sys->yuv_window,
857                                         GCGraphicsExposures, &xgcvalues );
858
859     XSetWindowBackground( p_vout->p_sys->p_display, p_vout->p_sys->yuv_window,
860              BlackPixel(p_vout->p_sys->p_display, p_vout->p_sys->i_screen ) );
861
862     XMapWindow( p_vout->p_sys->p_display, p_vout->p_sys->yuv_window );
863     XSelectInput( p_vout->p_sys->p_display, p_vout->p_sys->yuv_window,
864                   ExposureMask );
865
866
867     XVideoSetMousePointer( p_vout );
868
869     return( 0 );
870 }
871
872 static void XVideoDestroyWindow( vout_thread_t *p_vout )
873 {
874     XFreeGC( p_vout->p_sys->p_display, p_vout->p_sys->yuv_gc );
875     XDestroyWindow( p_vout->p_sys->p_display, p_vout->p_sys->yuv_window );
876
877     XUnmapWindow( p_vout->p_sys->p_display, p_vout->p_sys->window );
878     XFreeGC( p_vout->p_sys->p_display, p_vout->p_sys->gc );
879     XDestroyWindow( p_vout->p_sys->p_display, p_vout->p_sys->window );
880 }
881
882 /*****************************************************************************
883  * XVideoCreateShmImage: create an XvImage using shared memory extension
884  *****************************************************************************
885  * Prepare an XvImage for display function.
886  * The order of the operations respects the recommandations of the mit-shm
887  * document by J.Corbet and K.Packard. Most of the parameters were copied from
888  * there.
889  *****************************************************************************/
890 static int XVideoCreateShmImage( Display* dpy, int xv_port,
891                                     XvImage **pp_xvimage,
892                                     XShmSegmentInfo *p_shm_info,
893                                     int i_width, int i_height )
894 {
895     *pp_xvimage = XvShmCreateImage( dpy, xv_port,
896                                     GUID_YUV12_PLANAR, 0,
897                                     i_width, i_height,
898                                     p_shm_info );
899     if( !(*pp_xvimage) )
900     {
901         intf_ErrMsg( "vout error: XvShmCreateImage failed." );
902         return( -1 );
903     }
904
905     p_shm_info->shmid    = shmget( IPC_PRIVATE, (*pp_xvimage)->data_size,
906                                    IPC_CREAT | 0777 );
907     if( p_shm_info->shmid < 0)                                      /* error */
908     {
909         intf_ErrMsg( "vout error: cannot allocate shared image data (%s)",
910                     strerror(errno));
911         return( 1 );
912     }
913
914     p_shm_info->shmaddr  = (*pp_xvimage)->data = shmat( p_shm_info->shmid,
915                                                         0, 0 );
916     p_shm_info->readOnly = False;
917
918     /* Mark the shm segment to be removed when there will be no more
919      * attachements, so it is automatic on process exit or after shmdt */
920     shmctl( p_shm_info->shmid, IPC_RMID, 0 );
921
922     if( !XShmAttach( dpy, p_shm_info ) )
923     {
924         intf_ErrMsg( "vout error: XShmAttach failed" );
925         shmdt( p_shm_info->shmaddr );
926         return( -1 );
927     }
928
929     /* Send image to X server. This instruction is required, since having
930      * built a Shm XImage and not using it causes an error on XCloseDisplay */
931     XFlush( dpy );
932
933     return( 0 );
934 }
935
936 /*****************************************************************************
937  * XVideoDestroyShmImage
938  *****************************************************************************
939  * Destroy XImage AND associated data. Detach shared memory segment from
940  * server and process, then free it. If pointer is NULL, the image won't be
941  * destroyed (see vout_ManageOutputMethod())
942  *****************************************************************************/
943 static void XVideoDestroyShmImage( vout_thread_t *p_vout, XvImage *p_xvimage,
944                                    XShmSegmentInfo *p_shm_info )
945 {
946     /* If pointer is NULL, do nothing */
947     if( p_xvimage == NULL )
948     {
949         return;
950     }
951
952     XShmDetach( p_vout->p_sys->p_display, p_shm_info );/* detach from server */
953 #if 0
954     XDestroyImage( p_ximage ); /* XXX */
955 #endif
956
957     if( shmdt( p_shm_info->shmaddr ) )  /* detach shared memory from process */
958     {                                   /* also automatic freeing...         */
959         intf_ErrMsg( "vout error: cannot detach shared memory (%s)",
960                      strerror(errno) );
961     }
962 }
963
964 /*****************************************************************************
965  * XVideoEnableScreenSaver: enable screen saver
966  *****************************************************************************
967  * This function enable the screen saver on a display after it had been
968  * disabled by XDisableScreenSaver. Both functions use a counter mechanism to
969  * know wether the screen saver can be activated or not: if n successive calls
970  * are made to XDisableScreenSaver, n successive calls to XEnableScreenSaver
971  * will be required before the screen saver could effectively be activated.
972  *****************************************************************************/
973 void XVideoEnableScreenSaver( vout_thread_t *p_vout )
974 {
975     intf_DbgMsg( "intf: enabling screen saver" );
976     XSetScreenSaver( p_vout->p_sys->p_display, p_vout->p_sys->i_ss_timeout,
977                      p_vout->p_sys->i_ss_interval,
978                      p_vout->p_sys->i_ss_blanking,
979                      p_vout->p_sys->i_ss_exposure );
980 }
981
982 /*****************************************************************************
983  * XVideoDisableScreenSaver: disable screen saver
984  *****************************************************************************
985  * See XEnableScreenSaver
986  *****************************************************************************/
987 void XVideoDisableScreenSaver( vout_thread_t *p_vout )
988 {
989     /* Save screen saver informations */
990     XGetScreenSaver( p_vout->p_sys->p_display, &p_vout->p_sys->i_ss_timeout,
991                      &p_vout->p_sys->i_ss_interval,
992                      &p_vout->p_sys->i_ss_blanking,
993                      &p_vout->p_sys->i_ss_exposure );
994
995     /* Disable screen saver */
996     intf_DbgMsg( "intf: disabling screen saver" );
997     XSetScreenSaver( p_vout->p_sys->p_display, 0,
998                      p_vout->p_sys->i_ss_interval,
999                      p_vout->p_sys->i_ss_blanking,
1000                      p_vout->p_sys->i_ss_exposure );
1001 }
1002
1003 /*****************************************************************************
1004  * XVideoSetMousePointer: hide or show the mouse pointer
1005  *****************************************************************************
1006  * This function hides the X pointer if requested.
1007  *****************************************************************************/
1008 void XVideoSetMousePointer( const vout_thread_t *p_vout )
1009 {
1010     static Cursor blank_cursor;
1011     static boolean_t b_created_blank_cursor = 0;
1012
1013     if( !p_vout->p_sys->b_mouse_pointer_visible )
1014     {
1015         if( !b_created_blank_cursor )
1016         {
1017             XColor color;
1018             Pixmap blank = XCreatePixmap( p_vout->p_sys->p_display,
1019                                DefaultRootWindow(p_vout->p_sys->p_display),
1020                                1, 1, 1 );
1021
1022             XParseColor( p_vout->p_sys->p_display,
1023                          XCreateColormap( p_vout->p_sys->p_display,
1024                                           DefaultRootWindow(
1025                                                   p_vout->p_sys->p_display ),
1026                                           DefaultVisual(
1027                                                   p_vout->p_sys->p_display,
1028                                                   p_vout->p_sys->i_screen ),
1029                                           AllocNone ),
1030                          "black", &color );
1031
1032             blank_cursor = XCreatePixmapCursor( p_vout->p_sys->p_display,
1033                            blank, blank, &color, &color, 1, 1 );
1034
1035             b_created_blank_cursor = 1;
1036         }
1037         XDefineCursor( p_vout->p_sys->p_display,
1038                        p_vout->p_sys->window, blank_cursor );
1039     }
1040     else
1041     {
1042         XUndefineCursor( p_vout->p_sys->p_display, p_vout->p_sys->window );
1043     }
1044 }
1045
1046 /* This based on some code in SetBufferPicture... At the moment it's only
1047  * used by the xvideo plugin, but others may want to use it. */
1048 static void XVideoOutputCoords( const picture_t *p_pic, const boolean_t scale,
1049                                 const int win_w, const int win_h,
1050                                 int *dx, int *dy, int *w, int *h)
1051 {
1052     if( !scale )
1053     {
1054         *w = p_pic->i_width; *h = p_pic->i_height;
1055     }
1056     else
1057     {
1058         *w = win_w;
1059         switch( p_pic->i_aspect_ratio )
1060         {
1061             case AR_3_4_PICTURE:
1062                 *h = win_w * 3 / 4;
1063                 break;
1064
1065             case AR_16_9_PICTURE:
1066                 *h = win_w * 9 / 16;
1067                 break;
1068
1069             case AR_221_1_PICTURE:
1070                 *h = win_w * 100 / 221;
1071                 break;
1072
1073             case AR_SQUARE_PICTURE:
1074             default:
1075                 *h = win_w * p_pic->i_height / p_pic->i_width;
1076                 break;
1077         }
1078
1079         if( *h > win_h )
1080         {
1081             *h = win_h;
1082             switch( p_pic->i_aspect_ratio )
1083             {
1084                 case AR_3_4_PICTURE:
1085                     *w = win_h * 4 / 3;
1086                     break;
1087
1088                 case AR_16_9_PICTURE:
1089                     *w = win_h * 16 / 9;
1090                     break;
1091
1092                 case AR_221_1_PICTURE:
1093                     *w = win_h * 221 / 100;
1094                     break;
1095
1096                 case AR_SQUARE_PICTURE:
1097                 default:
1098                     *w = win_h * p_pic->i_width / p_pic->i_height;
1099                     break;
1100             }
1101         }
1102     }
1103
1104     /* Set picture position */
1105     *dx = (win_w - *w) / 2;
1106     *dy = (win_h - *h) / 2;
1107 }
1108
1109
1110 static int XVideoGetPort( Display *dpy )
1111 {
1112     int            i, i_adaptors;
1113     int            xv_port = -1;
1114     XvAdaptorInfo *adaptor_info;
1115
1116     switch( XvQueryAdaptors( dpy, DefaultRootWindow( dpy ),
1117                              &i_adaptors, &adaptor_info ) )
1118     {
1119         case Success:
1120             break;
1121
1122         case XvBadExtension:
1123             intf_ErrMsg( "vout error: XvBadExtension for XvQueryAdaptors" );
1124             return( -1 );
1125
1126         case XvBadAlloc:
1127             intf_ErrMsg( "vout error: XvBadAlloc for XvQueryAdaptors" );
1128             return( -1 );
1129
1130         default:
1131             intf_ErrMsg( "vout error: XvQueryAdaptors failed" );
1132             return( -1 );
1133     }
1134
1135     for( i=0; i < i_adaptors && xv_port == -1; ++i )
1136         if( ( adaptor_info[ i ].type & XvInputMask ) &&
1137             ( adaptor_info[ i ].type & XvImageMask ) )
1138         {
1139             /* check that port supports YUV12 planar format... */
1140             int port = adaptor_info[ i ].base_id;
1141             int i_num_formats, i;
1142             XvImageFormatValues *imageFormats;
1143
1144             imageFormats = XvListImageFormats( dpy, port, &i_num_formats );
1145
1146             for( i=0; i < i_num_formats && xv_port == -1; ++i )
1147                 if( imageFormats[ i ].id == GUID_YUV12_PLANAR )
1148                     xv_port = port;
1149
1150             if( xv_port == -1 )
1151                 intf_WarnMsg( 3, "vout: XVideo image input port %d "
1152                         "does not support the YUV12 planar format which is "
1153                         "currently required by the xvideo output plugin.",
1154                         port );
1155
1156             if( imageFormats )
1157                 XFree( imageFormats );
1158         }
1159
1160     if( i_adaptors > 0 )
1161         XvFreeAdaptorInfo(adaptor_info);
1162
1163     if( xv_port == -1 )
1164         intf_ErrMsg( "vout error: didn't find a suitable Xvideo image input port." );
1165
1166     return( xv_port );
1167 }
1168
1169
1170 /*****************************************************************************
1171  * XVideoDisplay: display image
1172  *****************************************************************************
1173  * This function displays the image stored in p_vout->p_sys->p_xvimage.
1174  * The image is scaled to fit in the output window (and to have the correct
1175  * aspect ratio).
1176  *****************************************************************************/
1177 static void XVideoDisplay( vout_thread_t *p_vout )
1178 {
1179     int     i_dest_width, i_dest_height, i_dest_x, i_dest_y;
1180
1181     if( !p_vout->p_sys->p_xvimage ) return;
1182
1183     XVideoOutputCoords( p_vout->p_rendered_pic, p_vout->b_scale,
1184                         p_vout->p_sys->i_window_width,
1185                         p_vout->p_sys->i_window_height,
1186                         &i_dest_x, &i_dest_y,
1187                         &i_dest_width, &i_dest_height);
1188
1189     XvShmPutImage( p_vout->p_sys->p_display, p_vout->p_sys->xv_port,
1190                    p_vout->p_sys->yuv_window, p_vout->p_sys->gc,
1191                    p_vout->p_sys->p_xvimage,
1192                    0 /*src_x*/, 0 /*src_y*/,
1193                    p_vout->p_rendered_pic->i_width,
1194                    p_vout->p_rendered_pic->i_height,
1195                    0 /*dest_x*/, 0 /*dest_y*/, i_dest_width, i_dest_height,
1196                    True );
1197
1198      XResizeWindow( p_vout->p_sys->p_display, p_vout->p_sys->yuv_window,
1199              i_dest_width, i_dest_height );
1200      XMoveWindow( p_vout->p_sys->p_display, p_vout->p_sys->yuv_window,
1201              i_dest_x, i_dest_y );
1202 }
1203
1204 #if 0
1205 /*****************************************************************************
1206  * XVideoSetAttribute
1207  *****************************************************************************
1208  * This function can be used to set attributes, e.g. XV_BRIGHTNESS and
1209  * XV_CONTRAST. "f_value" should be in the range of 0 to 1.
1210  *****************************************************************************/
1211 static void XVideoSetAttribute( vout_thread_t *p_vout,
1212                                 char *attr_name, float f_value )
1213 {
1214     int             i_attrib;
1215     XvAttribute    *p_attrib;
1216     Display        *p_dpy   = p_vout->p_sys->p_display;
1217     int             xv_port = p_vout->p_sys->xv_port;
1218
1219     p_attrib = XvQueryPortAttributes( p_dpy, xv_port, &i_attrib );
1220
1221     do
1222     {
1223         i_attrib--;
1224
1225         if( i_attrib >= 0 && !strcmp( p_attrib[ i_attrib ].name, attr_name ) )
1226         {
1227             int i_sv = f_value * ( p_attrib[ i_attrib ].max_value
1228                                     - p_attrib[ i_attrib ].min_value + 1 )
1229                         + p_attrib[ i_attrib ].min_value;
1230
1231             XvSetPortAttribute( p_dpy, xv_port,
1232                             XInternAtom( p_dpy, attr_name, False ), i_sv );
1233             break;
1234         }
1235
1236     } while( i_attrib > 0 );
1237
1238     if( p_attrib )
1239         XFree( p_attrib );
1240 }
1241 #endif