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