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