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