1 /*****************************************************************************
2 * vout_xvideo.c: Xvideo video output display method
3 *****************************************************************************
4 * Copyright (C) 1998-2001 VideoLAN
5 * $Id: vout_xvideo.c,v 1.33 2001/12/03 13:58:59 massiot Exp $
7 * Authors: Shane Harper <shanegh@optusnet.com.au>
8 * Vincent Seguin <seguin@via.ecp.fr>
9 * Samuel Hocevar <sam@zoy.org>
10 * David Kennedy <dkennedy@tinytoad.com>
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
25 *****************************************************************************/
27 #define MODULE_NAME xvideo
28 #include "modules_inner.h"
30 /*****************************************************************************
32 *****************************************************************************/
35 #include <errno.h> /* ENOMEM */
36 #include <stdlib.h> /* free() */
37 #include <string.h> /* strerror() */
39 #ifdef HAVE_MACHINE_PARAM_H
41 #include <machine/param.h>
42 #include <sys/types.h> /* typedef ushort */
47 #include <netinet/in.h> /* BSD: struct in_addr */
50 #include <sys/shm.h> /* shmget(), shmctl() */
52 #include <X11/Xutil.h>
53 #include <X11/keysym.h>
54 #include <X11/extensions/XShm.h>
55 #include <X11/extensions/Xv.h>
56 #include <X11/extensions/Xvlib.h>
57 #include <X11/extensions/dpms.h>
67 #include "video_output.h"
69 #include "interface.h"
71 #include "netutils.h" /* network_ChannelJoin */
75 #include "stream_control.h" /* needed by input_ext-intf.h... */
76 #include "input_ext-intf.h"
79 #include "modules_export.h"
81 #define GUID_YUV12_PLANAR 0x32315659
84 /*****************************************************************************
85 * vout_sys_t: video output X11 method descriptor
86 *****************************************************************************
87 * This structure is part of the video output thread descriptor.
88 * It describes the XVideo specific properties of an output thread.
89 *****************************************************************************/
90 typedef struct vout_sys_s
94 /* this plugin (currently) requires the SHM Ext... */
95 boolean_t b_shm; /* shared memory extension flag */
98 /* Internal settings and properties */
99 Display * p_display; /* display pointer */
100 int i_screen; /* screen number */
101 Window window; /* root window */
102 GC gc; /* graphic context instance handler */
103 Window yuv_window; /* sub-window for displaying yuv video
108 /* Display buffers and shared memory information */
109 /* Note: only 1 buffer... Xv ext does double buffering. */
113 /* i_image_width & i_image_height reflect the
114 * size of the XvImage. They are used by
115 * vout_Display() to check if the image to be
116 * displayed can use the current XvImage. */
117 XShmSegmentInfo shm_info; /* shared memory zone information */
119 /* X11 generic properties */
121 Atom wm_delete_window;
123 int i_window_width; /* width of main window */
124 int i_window_height; /* height of main window */
127 /* Screen saver properties */
128 int i_ss_timeout; /* timeout */
129 int i_ss_interval; /* interval between changes */
130 int i_ss_blanking; /* blanking mode */
131 int i_ss_exposure; /* exposure mode */
133 /* Mouse pointer properties */
134 boolean_t b_mouse_pointer_visible;
135 mtime_t i_time_mouse_last_moved; /* used to auto-hide pointer*/
136 Cursor blank_cursor; /* the hidden cursor */
137 Pixmap cursor_pixmap;
141 /* Fullscreen needs to be able to hide the wm decorations */
142 #define MWM_HINTS_DECORATIONS (1L << 1)
143 #define PROP_MWM_HINTS_ELEMENTS 5
144 typedef struct mwmhints_s
153 /*****************************************************************************
155 *****************************************************************************/
156 static int vout_Probe ( probedata_t * );
157 static int vout_Create ( vout_thread_t * );
158 static int vout_Init ( vout_thread_t * );
159 static void vout_End ( vout_thread_t * );
160 static void vout_Destroy ( vout_thread_t * );
161 static int vout_Manage ( vout_thread_t * );
162 static void vout_Display ( vout_thread_t * );
163 static void vout_SetPalette( vout_thread_t *, u16 *, u16 *, u16 *, u16 * );
165 static int XVideoCreateWindow ( vout_thread_t * );
166 static void XVideoDestroyWindow ( vout_thread_t *p_vout );
167 static int XVideoUpdateImgSizeIfRequired( vout_thread_t *p_vout );
168 static int XVideoCreateShmImage ( Display* dpy, int xv_port,
169 XvImage **pp_xvimage,
170 XShmSegmentInfo *p_shm_info,
171 int i_width, int i_height );
172 static void XVideoDestroyShmImage ( vout_thread_t *, XvImage *,
174 static void X11ToggleMousePointer ( vout_thread_t * );
175 static void XVideoEnableScreenSaver ( vout_thread_t * );
176 static void XVideoDisableScreenSaver ( vout_thread_t * );
177 /*static void XVideoSetAttribute ( vout_thread_t *, char *, float );*/
179 static int XVideoCheckForXv ( Display * );
180 static int XVideoGetPort ( Display * );
181 static void XVideoOutputCoords ( const picture_t *, const boolean_t,
182 const int, const int,
183 int *, int *, int *, int * );
184 static void XVideoDisplay ( vout_thread_t * );
186 /*****************************************************************************
187 * Functions exported as capabilities. They are declared as static so that
188 * we don't pollute the namespace too much.
189 *****************************************************************************/
190 void _M( vout_getfunctions )( function_list_t * p_function_list )
192 p_function_list->pf_probe = vout_Probe;
193 p_function_list->functions.vout.pf_create = vout_Create;
194 p_function_list->functions.vout.pf_init = vout_Init;
195 p_function_list->functions.vout.pf_end = vout_End;
196 p_function_list->functions.vout.pf_destroy = vout_Destroy;
197 p_function_list->functions.vout.pf_manage = vout_Manage;
198 p_function_list->functions.vout.pf_display = vout_Display;
199 p_function_list->functions.vout.pf_setpalette = vout_SetPalette;
202 /*****************************************************************************
203 * vout_Probe: probe the video driver and return a score
204 *****************************************************************************
205 * This returns a score to the plugin manager so that it can select the best
207 *****************************************************************************/
208 static int vout_Probe( probedata_t *p_data )
210 Display *p_display; /* display pointer */
213 /* Open display, unsing 'vlc_display' or DISPLAY environment variable */
214 psz_display = XDisplayName( main_GetPszVariable(VOUT_DISPLAY_VAR, NULL) );
215 p_display = XOpenDisplay( psz_display );
216 if( p_display == NULL ) /* error */
218 intf_WarnMsg( 3, "vout: Xvideo cannot open display %s", psz_display );
219 intf_WarnMsg( 3, "vout: Xvideo not supported" );
223 if( !XVideoCheckForXv( p_display ) )
225 intf_WarnMsg( 3, "vout: Xvideo not supported" );
226 XCloseDisplay( p_display );
230 if( XVideoGetPort( p_display ) < 0 )
232 intf_WarnMsg( 3, "vout: Xvideo not supported" );
233 XCloseDisplay( p_display );
237 /* Clean-up everyting */
238 XCloseDisplay( p_display );
240 if( TestMethod( VOUT_METHOD_VAR, "xvideo" ) )
248 /*****************************************************************************
249 * vout_Create: allocate XVideo video thread output method
250 *****************************************************************************
251 * This function allocate and initialize a XVideo vout method. It uses some of
252 * the vout properties to choose the window size, and change them according to
253 * the actual properties of the display.
254 *****************************************************************************/
255 static int vout_Create( vout_thread_t *p_vout )
260 /* Allocate structure */
261 p_vout->p_sys = malloc( sizeof( vout_sys_t ) );
262 if( p_vout->p_sys == NULL )
264 intf_ErrMsg( "vout error: %s", strerror(ENOMEM) );
268 /* Open display, unsing 'vlc_display' or DISPLAY environment variable */
269 psz_display = XDisplayName( main_GetPszVariable( VOUT_DISPLAY_VAR, NULL ) );
270 p_vout->p_sys->p_display = XOpenDisplay( psz_display );
272 if( p_vout->p_sys->p_display == NULL ) /* error */
274 intf_ErrMsg( "vout error: cannot open display %s", psz_display );
275 free( p_vout->p_sys );
278 p_vout->p_sys->i_screen = DefaultScreen( p_vout->p_sys->p_display );
281 = main_GetIntVariable( VOUT_FULLSCREEN_VAR, VOUT_FULLSCREEN_DEFAULT );
283 if( !XVideoCheckForXv( p_vout->p_sys->p_display ) )
285 intf_ErrMsg( "vout error: no XVideo extension" );
286 XCloseDisplay( p_vout->p_sys->p_display );
287 free( p_vout->p_sys );
291 /* Check we have access to a video port */
292 if( (p_vout->p_sys->xv_port = XVideoGetPort(p_vout->p_sys->p_display)) <0 )
294 intf_ErrMsg( "vout error: cannot get XVideo port" );
295 XCloseDisplay( p_vout->p_sys->p_display );
296 free( p_vout->p_sys );
299 intf_DbgMsg( "Using xv port %d" , p_vout->p_sys->xv_port );
301 /* Create blank cursor (for mouse cursor autohiding) */
302 p_vout->p_sys->b_mouse_pointer_visible = 1;
303 p_vout->p_sys->cursor_pixmap = XCreatePixmap( p_vout->p_sys->p_display,
305 p_vout->p_sys->p_display),
308 XParseColor( p_vout->p_sys->p_display,
309 XCreateColormap( p_vout->p_sys->p_display,
311 p_vout->p_sys->p_display ),
313 p_vout->p_sys->p_display,
314 p_vout->p_sys->i_screen ),
316 "black", &cursor_color );
318 p_vout->p_sys->blank_cursor = XCreatePixmapCursor(
319 p_vout->p_sys->p_display,
320 p_vout->p_sys->cursor_pixmap,
321 p_vout->p_sys->cursor_pixmap,
323 &cursor_color, 1, 1 );
325 /* Spawn base window - this window will include the video output window,
326 * but also command buttons, subtitles and other indicators */
327 if( XVideoCreateWindow( p_vout ) )
329 intf_ErrMsg( "vout error: cannot create XVideo window" );
330 XCloseDisplay( p_vout->p_sys->p_display );
331 free( p_vout->p_sys );
335 /* p_vout->pf_setbuffers( p_vout, NULL, NULL ); */
338 /* XXX The brightness and contrast values should be read from environment
339 * XXX variables... */
340 XVideoSetAttribute( p_vout, "XV_BRIGHTNESS", 0.5 );
341 XVideoSetAttribute( p_vout, "XV_CONTRAST", 0.5 );
344 /* Disable screen saver and return */
345 XVideoDisableScreenSaver( p_vout );
350 /*****************************************************************************
351 * vout_Init: initialize XVideo video thread output method
352 *****************************************************************************/
353 static int vout_Init( vout_thread_t *p_vout )
356 /* FIXME : As of 2001-03-16, XFree4 for MacOS X does not support Xshm. */
357 p_vout->p_sys->b_shm = 0;
359 p_vout->b_need_render = 0;
360 p_vout->p_sys->i_image_width = p_vout->p_sys->i_image_height = 0;
365 /*****************************************************************************
366 * vout_End: terminate XVideo video thread output method
367 *****************************************************************************
368 * Destroy the XvImage. It is called at the end of the thread, but also each
369 * time the image is resized.
370 *****************************************************************************/
371 static void vout_End( vout_thread_t *p_vout )
373 XVideoDestroyShmImage( p_vout, p_vout->p_sys->p_xvimage,
374 &p_vout->p_sys->shm_info );
377 /*****************************************************************************
378 * vout_Destroy: destroy XVideo video thread output method
379 *****************************************************************************
380 * Terminate an output method created by vout_Create
381 *****************************************************************************/
382 static void vout_Destroy( vout_thread_t *p_vout )
384 /* Restore cursor if it was blanked */
385 if( !p_vout->p_sys->b_mouse_pointer_visible )
386 X11ToggleMousePointer( p_vout );
388 /* Destroy blank cursor pixmap */
389 XFreePixmap( p_vout->p_sys->p_display, p_vout->p_sys->cursor_pixmap );
391 XVideoEnableScreenSaver( p_vout );
392 XVideoDestroyWindow( p_vout );
393 XCloseDisplay( p_vout->p_sys->p_display );
395 /* Destroy structure */
396 free( p_vout->p_sys );
399 /*****************************************************************************
400 * vout_Manage: handle X11 events
401 *****************************************************************************
402 * This function should be called regularly by video output thread. It manages
403 * X11 events and allows window resizing. It returns a non null value on
406 * XXX Should "factor-out" common code in this and the "same" fn in the x11
408 *****************************************************************************/
409 static int vout_Manage( vout_thread_t *p_vout )
411 XEvent xevent; /* X11 event */
412 char i_key; /* ISO Latin-1 key */
415 /* Handle X11 events: ConfigureNotify events are parsed to know if the
416 * output window's size changed, MapNotify and UnmapNotify to know if the
417 * window is mapped (and if the display is useful), and ClientMessages
418 * to intercept window destruction requests */
419 while( XCheckWindowEvent( p_vout->p_sys->p_display, p_vout->p_sys->window,
420 StructureNotifyMask | KeyPressMask |
421 ButtonPressMask | ButtonReleaseMask |
422 PointerMotionMask, &xevent )
425 /* ConfigureNotify event: prepare */
426 if( (xevent.type == ConfigureNotify)
427 /*&& ((xevent.xconfigure.width != p_vout->p_sys->i_window_width)
428 || (xevent.xconfigure.height != p_vout->p_sys->i_window_height))*/ )
430 /* Update dimensions */
431 p_vout->p_sys->i_window_width = xevent.xconfigure.width;
432 p_vout->p_sys->i_window_height = xevent.xconfigure.height;
433 // p_vout->i_changes |= VOUT_SIZE_CHANGE;
435 /* MapNotify event: change window status and disable screen saver */
436 else if( xevent.type == MapNotify)
438 if( (p_vout != NULL) && !p_vout->b_active )
440 XVideoDisableScreenSaver( p_vout );
441 p_vout->b_active = 1;
444 /* UnmapNotify event: change window status and enable screen saver */
445 else if( xevent.type == UnmapNotify )
447 if( (p_vout != NULL) && p_vout->b_active )
449 XVideoEnableScreenSaver( p_vout );
450 p_vout->b_active = 0;
454 else if( xevent.type == KeyPress )
456 /* We may have keys like F1 trough F12, ESC ... */
457 x_key_symbol = XKeycodeToKeysym( p_vout->p_sys->p_display,
458 xevent.xkey.keycode, 0 );
459 switch( x_key_symbol )
462 p_main->p_intf->b_die = 1;
465 p_main->p_intf->b_menu_change = 1;
469 * The reason why I use this instead of XK_0 is that
470 * with XLookupString, we don't have to care about
473 if( XLookupString( &xevent.xkey, &i_key, 1, NULL, NULL ) )
479 p_main->p_intf->b_die = 1;
483 p_vout->i_changes |= VOUT_FULLSCREEN_CHANGE;
486 network_ChannelJoin( 0 );
489 network_ChannelJoin( 1 );
492 network_ChannelJoin( 2 );
495 network_ChannelJoin( 3 );
498 network_ChannelJoin( 4 );
501 network_ChannelJoin( 5 );
504 network_ChannelJoin( 6 );
507 network_ChannelJoin( 7 );
510 network_ChannelJoin( 8 );
513 network_ChannelJoin( 9 );
516 if( intf_ProcessKey( p_main->p_intf,
519 intf_DbgMsg( "unhandled key '%c' (%i)",
520 (char)i_key, i_key );
529 else if( xevent.type == ButtonPress )
531 switch( ((XButtonEvent *)&xevent)->button )
534 /* in this part we will eventually manage
535 * clicks for DVD navigation for instance */
540 else if( xevent.type == ButtonRelease )
542 switch( ((XButtonEvent *)&xevent)->button )
545 /* FIXME: need locking ! */
546 p_main->p_intf->b_menu_change = 1;
551 else if( xevent.type == MotionNotify )
553 p_vout->p_sys->i_time_mouse_last_moved = mdate();
554 if( !p_vout->p_sys->b_mouse_pointer_visible )
555 X11ToggleMousePointer( p_vout );
560 intf_WarnMsg( 3, "%p -> unhandled event type %d received",
561 p_vout, xevent.type );
565 /* Handle events for YUV video output sub-window */
566 while( XCheckWindowEvent( p_vout->p_sys->p_display,
567 p_vout->p_sys->yuv_window,
568 ExposureMask, &xevent ) == True )
570 /* Window exposed (only handled if stream playback is paused) */
571 if( xevent.type == Expose )
573 if( ((XExposeEvent *)&xevent)->count == 0 )
574 /* (if this is the last a collection of expose events...) */
575 if( p_main->p_intf->p_input )
577 p_main->p_intf->p_input->stream.control.i_status )
578 XVideoDisplay( p_vout );
582 /* ClientMessage event - only WM_PROTOCOLS with WM_DELETE_WINDOW data
583 * are handled - according to the man pages, the format is always 32
585 while( XCheckTypedEvent( p_vout->p_sys->p_display,
586 ClientMessage, &xevent ) )
588 if( (xevent.xclient.message_type == p_vout->p_sys->wm_protocols)
589 && (xevent.xclient.data.l[0] == p_vout->p_sys->wm_delete_window ) )
591 p_main->p_intf->b_die = 1;
595 intf_DbgMsg( "%p -> unhandled ClientMessage received", p_vout );
599 if ( p_vout->i_changes & VOUT_FULLSCREEN_CHANGE )
601 intf_DbgMsg( "vout: changing full-screen status" );
603 p_vout->b_fullscreen = !p_vout->b_fullscreen;
605 /* Get rid of the old window */
606 XVideoDestroyWindow( p_vout );
608 /* And create a new one */
609 if( XVideoCreateWindow( p_vout ) )
611 intf_ErrMsg( "vout error: cannot create X11 window" );
612 XCloseDisplay( p_vout->p_sys->p_display );
614 free( p_vout->p_sys );
620 if( (p_vout->i_changes & VOUT_GRAYSCALE_CHANGE))
622 /* FIXME: clear flags ?? */
628 if( p_vout->i_changes & VOUT_SIZE_CHANGE )
630 intf_DbgMsg( "vout: resizing window" );
631 p_vout->i_changes &= ~VOUT_SIZE_CHANGE;
633 p_vout->i_width = p_vout->p_sys->i_window_width;
634 p_vout->i_height = p_vout->p_sys->i_window_height;
636 intf_WarnMsg( 3, "vout: video display resized (%dx%d)",
637 p_vout->i_width, p_vout->i_height );
640 /* Autohide Cursor */
641 if( p_vout->p_sys->b_mouse_pointer_visible &&
642 mdate() - p_vout->p_sys->i_time_mouse_last_moved > 2000000 )
644 X11ToggleMousePointer( p_vout );
650 /*****************************************************************************
651 * vout_Display: displays previously rendered output
652 *****************************************************************************
653 * This function sends the currently rendered image to X11 server.
654 * (The Xv extension takes care of "double-buffering".)
655 *****************************************************************************/
656 static void vout_Display( vout_thread_t *p_vout )
658 boolean_t b_draw = 1;
659 int i_size = p_vout->p_rendered_pic->i_width *
660 p_vout->p_rendered_pic->i_height;
662 if( XVideoUpdateImgSizeIfRequired( p_vout ) )
667 switch( p_vout->p_rendered_pic->i_type )
669 case YUV_422_PICTURE:
670 intf_ErrMsg( "vout error: YUV_422_PICTURE not (yet) supported" );
674 case YUV_444_PICTURE:
675 intf_ErrMsg( "vout error: YUV_444_PICTURE not (yet) supported" );
679 case YUV_420_PICTURE:
680 memcpy( p_vout->p_sys->p_xvimage->data,
681 p_vout->p_rendered_pic->p_y, i_size );
682 memcpy( p_vout->p_sys->p_xvimage->data + ( i_size ),
683 p_vout->p_rendered_pic->p_v, i_size / 4 );
684 memcpy( p_vout->p_sys->p_xvimage->data + ( i_size ) + ( i_size / 4 ),
685 p_vout->p_rendered_pic->p_u, i_size / 4 );
691 XVideoDisplay( p_vout );
695 static void vout_SetPalette( p_vout_thread_t p_vout,
696 u16 *red, u16 *green, u16 *blue, u16 *transp )
701 /* following functions are local */
703 /*****************************************************************************
704 * XVideoUpdateImgSizeIfRequired
705 *****************************************************************************
706 * This function checks to see if the image to be displayed is of a different
707 * size to the last image displayed. If so, the old shm block must be
708 * destroyed and a new one created.
709 * Note: the "image size" is the size of the image to be passed to the Xv
710 * extension (which is probably different to the size of the output window).
711 *****************************************************************************/
712 static int XVideoUpdateImgSizeIfRequired( vout_thread_t *p_vout )
714 int i_img_width = p_vout->p_rendered_pic->i_width;
715 int i_img_height = p_vout->p_rendered_pic->i_height;
717 if( p_vout->p_sys->i_image_width != i_img_width
718 || p_vout->p_sys->i_image_height != i_img_height )
720 if( p_vout->p_sys->i_image_width != 0
721 && p_vout->p_sys->i_image_height != 0 )
723 /* Destroy XvImage to change its size */
727 p_vout->p_sys->i_image_width = i_img_width;
728 p_vout->p_sys->i_image_height = i_img_height;
730 /* Create XvImage using XShm extension */
731 if( XVideoCreateShmImage( p_vout->p_sys->p_display,
732 p_vout->p_sys->xv_port,
733 &p_vout->p_sys->p_xvimage,
734 &p_vout->p_sys->shm_info,
735 i_img_width, i_img_height ) )
737 intf_ErrMsg( "vout: failed to create xvimage." );
738 p_vout->p_sys->i_image_width = 0;
742 /* Set bytes per line and initialize buffers */
743 p_vout->i_bytes_per_line =
744 (p_vout->p_sys->p_xvimage->data_size) /
745 (p_vout->p_sys->p_xvimage->height);
752 /*****************************************************************************
753 * XVideoCheckForXv: check for the XVideo extension
754 *****************************************************************************/
755 static int XVideoCheckForXv( Display *dpy )
759 switch( XvQueryExtension( dpy, &i, &i, &i, &i, &i ) )
765 intf_WarnMsg( 3, "vout error: XvBadExtension" );
769 intf_WarnMsg( 3, "vout error: XvBadAlloc" );
773 intf_WarnMsg( 3, "vout error: XvQueryExtension failed" );
778 /*****************************************************************************
779 * XVideoCreateWindow: open and set-up XVideo main window
780 *****************************************************************************/
781 static int XVideoCreateWindow( vout_thread_t *p_vout )
783 XSizeHints xsize_hints;
784 XSetWindowAttributes xwindow_attributes;
791 boolean_t b_configure_notify;
792 boolean_t b_map_notify;
795 /* Set main window's size */
796 /* If we're full screen, we're full screen! */
797 if( p_vout->b_fullscreen )
799 p_vout->p_sys->i_window_width = DisplayWidth( p_vout->p_sys->p_display,
800 p_vout->p_sys->i_screen );
801 p_vout->p_sys->i_window_height = DisplayHeight( p_vout->p_sys->p_display,
802 p_vout->p_sys->i_screen );
806 p_vout->p_sys->i_window_width = p_vout->i_width;
807 p_vout->p_sys->i_window_height = p_vout->i_height;
810 /* Prepare window manager hints and properties */
811 xsize_hints.base_width = p_vout->p_sys->i_window_width;
812 xsize_hints.base_height = p_vout->p_sys->i_window_height;
813 xsize_hints.flags = PSize;
814 p_vout->p_sys->wm_protocols = XInternAtom( p_vout->p_sys->p_display,
815 "WM_PROTOCOLS", True );
816 p_vout->p_sys->wm_delete_window = XInternAtom( p_vout->p_sys->p_display,
817 "WM_DELETE_WINDOW", True );
819 /* Prepare window attributes */
820 xwindow_attributes.background_pixel = BlackPixel( p_vout->p_sys->p_display,
821 p_vout->p_sys->i_screen );
823 xwindow_attributes.event_mask = ExposureMask | StructureNotifyMask;
825 /* Create the window and set hints - the window must receive ConfigureNotify
826 * events, and, until it is displayed, Expose and MapNotify events. */
827 p_vout->p_sys->window =
828 XCreateWindow( p_vout->p_sys->p_display,
829 DefaultRootWindow( p_vout->p_sys->p_display ),
831 p_vout->p_sys->i_window_width,
832 p_vout->p_sys->i_window_height, 1,
834 CWBackPixel | CWEventMask,
835 &xwindow_attributes );
837 if ( p_vout->b_fullscreen )
839 prop = XInternAtom(p_vout->p_sys->p_display, "_MOTIF_WM_HINTS", False);
840 mwmhints.flags = MWM_HINTS_DECORATIONS;
841 mwmhints.decorations = 0;
842 XChangeProperty( p_vout->p_sys->p_display, p_vout->p_sys->window,
843 prop, prop, 32, PropModeReplace,
844 (unsigned char *)&mwmhints, PROP_MWM_HINTS_ELEMENTS );
846 XSetTransientForHint( p_vout->p_sys->p_display,
847 p_vout->p_sys->window, None );
848 XRaiseWindow( p_vout->p_sys->p_display, p_vout->p_sys->window );
852 /* Set window manager hints and properties: size hints, command,
853 * window's name, and accepted protocols */
854 XSetWMNormalHints( p_vout->p_sys->p_display, p_vout->p_sys->window,
856 XSetCommand( p_vout->p_sys->p_display, p_vout->p_sys->window,
857 p_main->ppsz_argv, p_main->i_argc );
858 XStoreName( p_vout->p_sys->p_display, p_vout->p_sys->window,
859 VOUT_TITLE " (XVideo output)" );
861 if( (p_vout->p_sys->wm_protocols == None) /* use WM_DELETE_WINDOW */
862 || (p_vout->p_sys->wm_delete_window == None)
863 || !XSetWMProtocols( p_vout->p_sys->p_display, p_vout->p_sys->window,
864 &p_vout->p_sys->wm_delete_window, 1 ) )
866 /* WM_DELETE_WINDOW is not supported by window manager */
867 intf_Msg( "vout error: missing or bad window manager" );
870 /* Creation of a graphic context that doesn't generate a GraphicsExpose
871 * event when using functions like XCopyArea */
872 xgcvalues.graphics_exposures = False;
873 p_vout->p_sys->gc = XCreateGC( p_vout->p_sys->p_display,
874 p_vout->p_sys->window,
875 GCGraphicsExposures, &xgcvalues);
877 /* Send orders to server, and wait until window is displayed - three
878 * events must be received: a MapNotify event, an Expose event allowing
879 * drawing in the window, and a ConfigureNotify to get the window
880 * dimensions. Once those events have been received, only ConfigureNotify
881 * events need to be received. */
883 b_configure_notify = 0;
885 XMapWindow( p_vout->p_sys->p_display, p_vout->p_sys->window);
888 XNextEvent( p_vout->p_sys->p_display, &xevent);
889 if( (xevent.type == Expose)
890 && (xevent.xexpose.window == p_vout->p_sys->window) )
894 else if( (xevent.type == MapNotify)
895 && (xevent.xmap.window == p_vout->p_sys->window) )
899 else if( (xevent.type == ConfigureNotify)
900 && (xevent.xconfigure.window == p_vout->p_sys->window) )
902 b_configure_notify = 1;
903 p_vout->p_sys->i_window_width = xevent.xconfigure.width;
904 p_vout->p_sys->i_window_height = xevent.xconfigure.height;
906 } while( !( b_expose && b_configure_notify && b_map_notify ) );
908 XSelectInput( p_vout->p_sys->p_display, p_vout->p_sys->window,
909 StructureNotifyMask | KeyPressMask |
910 ButtonPressMask | ButtonReleaseMask |
913 if( p_vout->b_fullscreen )
915 XSetInputFocus( p_vout->p_sys->p_display, p_vout->p_sys->window,
916 RevertToNone, CurrentTime );
917 XMoveWindow( p_vout->p_sys->p_display, p_vout->p_sys->window, 0, 0 );
920 /* Create YUV output sub-window. */
921 p_vout->p_sys->yuv_window=XCreateSimpleWindow( p_vout->p_sys->p_display,
922 p_vout->p_sys->window, 0, 0, 1, 1, 0,
923 BlackPixel( p_vout->p_sys->p_display,
924 p_vout->p_sys->i_screen ),
925 WhitePixel( p_vout->p_sys->p_display,
926 p_vout->p_sys->i_screen ) );
928 p_vout->p_sys->yuv_gc = XCreateGC( p_vout->p_sys->p_display,
929 p_vout->p_sys->yuv_window,
930 GCGraphicsExposures, &xgcvalues );
932 XSetWindowBackground( p_vout->p_sys->p_display, p_vout->p_sys->yuv_window,
933 BlackPixel(p_vout->p_sys->p_display, p_vout->p_sys->i_screen ) );
935 XMapWindow( p_vout->p_sys->p_display, p_vout->p_sys->yuv_window );
936 XSelectInput( p_vout->p_sys->p_display, p_vout->p_sys->yuv_window,
939 /* If the cursor was formerly blank than blank it again */
940 if( !p_vout->p_sys->b_mouse_pointer_visible )
942 X11ToggleMousePointer( p_vout );
943 X11ToggleMousePointer( p_vout );
946 XSync( p_vout->p_sys->p_display, False );
951 static void XVideoDestroyWindow( vout_thread_t *p_vout )
953 XSync( p_vout->p_sys->p_display, False );
955 XFreeGC( p_vout->p_sys->p_display, p_vout->p_sys->yuv_gc );
956 XDestroyWindow( p_vout->p_sys->p_display, p_vout->p_sys->yuv_window );
958 XUnmapWindow( p_vout->p_sys->p_display, p_vout->p_sys->window );
959 XFreeGC( p_vout->p_sys->p_display, p_vout->p_sys->gc );
960 XDestroyWindow( p_vout->p_sys->p_display, p_vout->p_sys->window );
963 /*****************************************************************************
964 * XVideoCreateShmImage: create an XvImage using shared memory extension
965 *****************************************************************************
966 * Prepare an XvImage for display function.
967 * The order of the operations respects the recommandations of the mit-shm
968 * document by J.Corbet and K.Packard. Most of the parameters were copied from
970 *****************************************************************************/
971 static int XVideoCreateShmImage( Display* dpy, int xv_port,
972 XvImage **pp_xvimage,
973 XShmSegmentInfo *p_shm_info,
974 int i_width, int i_height )
976 *pp_xvimage = XvShmCreateImage( dpy, xv_port,
977 GUID_YUV12_PLANAR, 0,
982 intf_ErrMsg( "vout error: XvShmCreateImage failed." );
986 p_shm_info->shmid = shmget( IPC_PRIVATE, (*pp_xvimage)->data_size,
988 if( p_shm_info->shmid < 0) /* error */
990 intf_ErrMsg( "vout error: cannot allocate shared image data (%s)",
995 p_shm_info->shmaddr = (*pp_xvimage)->data = shmat( p_shm_info->shmid,
997 p_shm_info->readOnly = False;
999 if( !XShmAttach( dpy, p_shm_info ) )
1001 intf_ErrMsg( "vout error: XShmAttach failed" );
1002 shmctl( p_shm_info->shmid, IPC_RMID, 0 );
1003 shmdt( p_shm_info->shmaddr );
1007 /* Send image to X server. This instruction is required, since having
1008 * built a Shm XImage and not using it causes an error on XCloseDisplay */
1009 XSync( dpy, False );
1011 /* Mark the shm segment to be removed when there will be no more
1012 * attachements, so it is automatic on process exit or after shmdt */
1013 shmctl( p_shm_info->shmid, IPC_RMID, 0 );
1018 /*****************************************************************************
1019 * XVideoDestroyShmImage
1020 *****************************************************************************
1021 * Destroy XImage AND associated data. Detach shared memory segment from
1022 * server and process, then free it. If pointer is NULL, the image won't be
1023 * destroyed (see vout_ManageOutputMethod())
1024 *****************************************************************************/
1025 static void XVideoDestroyShmImage( vout_thread_t *p_vout, XvImage *p_xvimage,
1026 XShmSegmentInfo *p_shm_info )
1028 /* If pointer is NULL, do nothing */
1029 if( p_xvimage == NULL )
1034 XSync( p_vout->p_sys->p_display, False );
1035 XShmDetach( p_vout->p_sys->p_display, p_shm_info );/* detach from server */
1037 XDestroyImage( p_ximage ); /* XXX */
1041 if( shmdt( p_shm_info->shmaddr ) ) /* detach shared memory from process */
1043 intf_ErrMsg( "vout error: cannot detach shared memory (%s)",
1048 /*****************************************************************************
1049 * XVideoEnableScreenSaver: enable screen saver
1050 *****************************************************************************
1051 * This function enable the screen saver on a display after it had been
1052 * disabled by XDisableScreenSaver. Both functions use a counter mechanism to
1053 * know wether the screen saver can be activated or not: if n successive calls
1054 * are made to XDisableScreenSaver, n successive calls to XEnableScreenSaver
1055 * will be required before the screen saver could effectively be activated.
1056 *****************************************************************************/
1057 void XVideoEnableScreenSaver( vout_thread_t *p_vout )
1059 intf_DbgMsg( "intf: enabling screen saver" );
1060 XSetScreenSaver( p_vout->p_sys->p_display, p_vout->p_sys->i_ss_timeout,
1061 p_vout->p_sys->i_ss_interval,
1062 p_vout->p_sys->i_ss_blanking,
1063 p_vout->p_sys->i_ss_exposure );
1065 DPMSEnable( p_vout->p_sys->p_display );
1068 /*****************************************************************************
1069 * XVideoDisableScreenSaver: disable screen saver
1070 *****************************************************************************
1071 * See XEnableScreenSaver
1072 *****************************************************************************/
1073 void XVideoDisableScreenSaver( vout_thread_t *p_vout )
1075 /* Save screen saver informations */
1076 XGetScreenSaver( p_vout->p_sys->p_display, &p_vout->p_sys->i_ss_timeout,
1077 &p_vout->p_sys->i_ss_interval,
1078 &p_vout->p_sys->i_ss_blanking,
1079 &p_vout->p_sys->i_ss_exposure );
1081 /* Disable screen saver */
1082 intf_DbgMsg( "intf: disabling screen saver" );
1083 XSetScreenSaver( p_vout->p_sys->p_display, 0,
1084 p_vout->p_sys->i_ss_interval,
1085 p_vout->p_sys->i_ss_blanking,
1086 p_vout->p_sys->i_ss_exposure );
1088 DPMSDisable( p_vout->p_sys->p_display );
1091 /*****************************************************************************
1092 * X11ToggleMousePointer: hide or show the mouse pointer
1093 *****************************************************************************
1094 * This function hides the X pointer if requested.
1095 *****************************************************************************/
1096 void X11ToggleMousePointer( vout_thread_t *p_vout )
1099 if( p_vout->p_sys->b_mouse_pointer_visible )
1101 XDefineCursor( p_vout->p_sys->p_display,
1102 p_vout->p_sys->window,
1103 p_vout->p_sys->blank_cursor );
1104 p_vout->p_sys->b_mouse_pointer_visible = 0;
1108 XUndefineCursor( p_vout->p_sys->p_display, p_vout->p_sys->window );
1109 p_vout->p_sys->b_mouse_pointer_visible = 1;
1113 /* This based on some code in SetBufferPicture... At the moment it's only
1114 * used by the xvideo plugin, but others may want to use it. */
1115 static void XVideoOutputCoords( const picture_t *p_pic, const boolean_t scale,
1116 const int win_w, const int win_h,
1117 int *dx, int *dy, int *w, int *h )
1122 /*****************************************************************************
1123 * XVideoGetPort: get YUV12 port
1124 *****************************************************************************
1126 *****************************************************************************/
1127 static int XVideoGetPort( Display *dpy )
1129 XvAdaptorInfo *p_adaptor;
1130 int i_adaptor, i_num_adaptors, i_requested_adaptor;
1131 int i_selected_port;
1133 switch( XvQueryAdaptors( dpy, DefaultRootWindow( dpy ),
1134 &i_num_adaptors, &p_adaptor ) )
1139 case XvBadExtension:
1140 intf_WarnMsg( 3, "vout error: XvBadExtension for XvQueryAdaptors" );
1144 intf_WarnMsg( 3, "vout error: XvBadAlloc for XvQueryAdaptors" );
1148 intf_WarnMsg( 3, "vout error: XvQueryAdaptors failed" );
1152 i_selected_port = -1;
1153 i_requested_adaptor = main_GetIntVariable( VOUT_XVADAPTOR_VAR, -1 );
1155 /* No special xv port has been requested so try all of them */
1156 for( i_adaptor = 0; i_adaptor < i_num_adaptors; ++i_adaptor )
1160 /* If we requested an adaptor and it's not this one, we aren't
1162 if( i_requested_adaptor != -1 && i_adaptor != i_requested_adaptor )
1167 /* If the adaptor doesn't have the required properties, skip it */
1168 if( !( p_adaptor[ i_adaptor ].type & XvInputMask ) ||
1169 !( p_adaptor[ i_adaptor ].type & XvImageMask ) )
1174 for( i_port = p_adaptor[i_adaptor].base_id;
1175 i_port < p_adaptor[i_adaptor].base_id
1176 + p_adaptor[i_adaptor].num_ports;
1179 XvImageFormatValues *p_formats;
1180 int i_format, i_num_formats;
1182 /* If we already found a port, we aren't interested */
1183 if( i_selected_port != -1 )
1188 /* Check that port supports YUV12 planar format... */
1189 p_formats = XvListImageFormats( dpy, i_port, &i_num_formats );
1191 for( i_format = 0; i_format < i_num_formats; i_format++ )
1193 XvEncodingInfo *p_enc;
1194 int i_enc, i_num_encodings;
1195 XvAttribute *p_attr;
1196 int i_attr, i_num_attributes;
1198 if( p_formats[ i_format ].id != GUID_YUV12_PLANAR )
1203 /* Found a matching port, print a description of this port */
1204 i_selected_port = i_port;
1206 intf_WarnMsg( 3, "vout: XVideoGetPort found adaptor %i port %i",
1208 intf_WarnMsg( 3, " image format 0x%x (%4.4s) %s supported",
1209 p_formats[ i_format ].id,
1210 (char *)&p_formats[ i_format ].id,
1211 ( p_formats[ i_format ].format
1212 == XvPacked ) ? "packed" : "planar" );
1214 intf_WarnMsg( 4, " encoding list:" );
1216 if( XvQueryEncodings( dpy, i_port, &i_num_encodings, &p_enc )
1219 intf_WarnMsg( 4, " XvQueryEncodings failed" );
1223 for( i_enc = 0; i_enc < i_num_encodings; i_enc++ )
1225 intf_WarnMsg( 4, " id=%ld, name=%s, size=%ldx%ld,"
1226 " numerator=%d, denominator=%d",
1227 p_enc[i_enc].encoding_id, p_enc[i_enc].name,
1228 p_enc[i_enc].width, p_enc[i_enc].height,
1229 p_enc[i_enc].rate.numerator,
1230 p_enc[i_enc].rate.denominator );
1235 XvFreeEncodingInfo( p_enc );
1238 intf_WarnMsg( 4, " attribute list:" );
1239 p_attr = XvQueryPortAttributes( dpy, i_port,
1240 &i_num_attributes );
1241 for( i_attr = 0; i_attr < i_num_attributes; i_attr++ )
1244 " name=%s, flags=[%s%s ], min=%i, max=%i",
1245 p_attr[i_attr].name,
1246 (p_attr[i_attr].flags & XvGettable) ? " get" : "",
1247 (p_attr[i_attr].flags & XvSettable) ? " set" : "",
1248 p_attr[i_attr].min_value, p_attr[i_attr].max_value );
1251 if( p_attr != NULL )
1257 if( p_formats != NULL )
1264 if( i_num_adaptors > 0 )
1266 XvFreeAdaptorInfo( p_adaptor );
1269 if( i_selected_port == -1 )
1271 if( i_requested_adaptor == -1 )
1273 intf_WarnMsg( 3, "vout: no XVideo port found supporting YUV12" );
1277 intf_WarnMsg( 3, "vout: XVideo adaptor %i does not support YUV12",
1278 i_requested_adaptor );
1282 return( i_selected_port );
1286 /*****************************************************************************
1287 * XVideoDisplay: display image
1288 *****************************************************************************
1289 * This function displays the image stored in p_vout->p_sys->p_xvimage.
1290 * The image is scaled to fit in the output window (and to have the correct
1292 *****************************************************************************/
1293 static void XVideoDisplay( vout_thread_t *p_vout )
1295 int i_dest_width, i_dest_height;
1296 int i_dest_x, i_dest_y;
1298 if( !p_vout->p_sys->p_xvimage || !p_vout->p_rendered_pic )
1303 i_dest_height = p_vout->p_sys->i_window_height >
1304 p_vout->p_rendered_pic->i_height
1305 ? p_vout->p_sys->i_window_height
1306 : p_vout->p_rendered_pic->i_height;
1307 i_dest_width = p_vout->p_sys->i_window_width >
1308 p_vout->p_rendered_pic->i_width
1309 ? p_vout->p_sys->i_window_width
1310 : p_vout->p_rendered_pic->i_width;
1312 if( p_vout->b_scale )
1314 int i_ratio = 900 * i_dest_width / i_dest_height;
1316 switch( p_vout->p_rendered_pic->i_aspect_ratio )
1318 case AR_3_4_PICTURE:
1319 if( i_ratio < 1200 )
1321 i_dest_width = i_dest_height * 4 / 3;
1325 i_dest_height = i_dest_width * 3 / 4;
1330 case AR_16_9_PICTURE:
1331 if( i_ratio < 1600 )
1333 i_dest_width = i_dest_height * 16 / 9;
1337 i_dest_height = i_dest_width * 9 / 16;
1342 case AR_221_1_PICTURE:
1343 if( i_ratio < 1989 )
1345 i_dest_width = i_dest_height * 221 / 100;
1349 i_dest_height = i_dest_width * 100 / 221;
1354 case AR_SQUARE_PICTURE:
1358 i_dest_width = i_dest_height * p_vout->p_rendered_pic->i_width / p_vout->p_rendered_pic->i_height;
1362 i_dest_height = i_dest_width * p_vout->p_rendered_pic->i_height / p_vout->p_rendered_pic->i_width;
1369 DisplayWidth( p_vout->p_sys->p_display, p_vout->p_sys->i_screen ) )
1371 i_dest_width = DisplayWidth( p_vout->p_sys->p_display,
1372 p_vout->p_sys->i_screen );
1373 i_dest_height = 900 * i_dest_width / i_ratio;
1375 else if( i_dest_height >
1376 DisplayHeight( p_vout->p_sys->p_display, p_vout->p_sys->i_screen ) )
1378 i_dest_height = DisplayHeight( p_vout->p_sys->p_display,
1379 p_vout->p_sys->i_screen );
1380 i_dest_width = i_ratio * i_dest_height / 900;
1384 XvShmPutImage( p_vout->p_sys->p_display, p_vout->p_sys->xv_port,
1385 p_vout->p_sys->yuv_window, p_vout->p_sys->gc,
1386 p_vout->p_sys->p_xvimage,
1387 0 /*src_x*/, 0 /*src_y*/,
1388 p_vout->p_rendered_pic->i_width,
1389 p_vout->p_rendered_pic->i_height,
1390 0 /*dest_x*/, 0 /*dest_y*/, i_dest_width, i_dest_height,
1394 XResizeWindow( p_vout->p_sys->p_display, p_vout->p_sys->yuv_window,
1395 i_dest_width, i_dest_height );
1398 if( ( ( i_dest_width != p_vout->p_sys->i_window_width ) ||
1399 ( i_dest_height != p_vout->p_sys->i_window_height ) ) &&
1400 ! p_vout->b_fullscreen )
1402 p_vout->p_sys->i_window_width = i_dest_width;
1403 p_vout->p_sys->i_window_height = i_dest_height;
1404 // p_vout->i_changes |= VOUT_SIZE_CHANGE;
1405 XResizeWindow( p_vout->p_sys->p_display, p_vout->p_sys->window,
1406 i_dest_width, i_dest_height );
1409 /* Set picture position */
1410 i_dest_x = (p_vout->p_sys->i_window_width - i_dest_width) / 2;
1411 i_dest_y = (p_vout->p_sys->i_window_height - i_dest_height) / 2;
1413 XMoveWindow( p_vout->p_sys->p_display, p_vout->p_sys->yuv_window,
1414 i_dest_x, i_dest_y );
1416 /* Send the order to the X server */
1417 XSync( p_vout->p_sys->p_display, False );
1421 /*****************************************************************************
1422 * XVideoSetAttribute
1423 *****************************************************************************
1424 * This function can be used to set attributes, e.g. XV_BRIGHTNESS and
1425 * XV_CONTRAST. "f_value" should be in the range of 0 to 1.
1426 *****************************************************************************/
1427 static void XVideoSetAttribute( vout_thread_t *p_vout,
1428 char *attr_name, float f_value )
1431 XvAttribute *p_attrib;
1432 Display *p_dpy = p_vout->p_sys->p_display;
1433 int xv_port = p_vout->p_sys->xv_port;
1435 p_attrib = XvQueryPortAttributes( p_dpy, xv_port, &i_attrib );
1441 if( i_attrib >= 0 && !strcmp( p_attrib[ i_attrib ].name, attr_name ) )
1443 int i_sv = f_value * ( p_attrib[ i_attrib ].max_value
1444 - p_attrib[ i_attrib ].min_value + 1 )
1445 + p_attrib[ i_attrib ].min_value;
1447 XvSetPortAttribute( p_dpy, xv_port,
1448 XInternAtom( p_dpy, attr_name, False ), i_sv );
1452 } while( i_attrib > 0 );