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