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