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