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