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