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