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