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