1 /*****************************************************************************
2 * vout_xvideo.c: Xvideo video output display method
3 *****************************************************************************
4 * Copyright (C) 1998-2001 VideoLAN
5 * $Id: vout_xvideo.c,v 1.35 2001/12/06 10:53:42 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. For the
536 * moment just pause the stream. */
537 input_SetStatus( p_main->p_intf->p_input,
538 INPUT_STATUS_PAUSE );
543 else if( xevent.type == ButtonRelease )
545 switch( ((XButtonEvent *)&xevent)->button )
548 /* FIXME: need locking ! */
549 p_main->p_intf->b_menu_change = 1;
554 else if( xevent.type == MotionNotify )
556 p_vout->p_sys->i_time_mouse_last_moved = mdate();
557 if( !p_vout->p_sys->b_mouse_pointer_visible )
558 X11ToggleMousePointer( p_vout );
563 intf_WarnMsg( 3, "%p -> unhandled event type %d received",
564 p_vout, xevent.type );
568 /* Handle events for YUV video output sub-window */
569 while( XCheckWindowEvent( p_vout->p_sys->p_display,
570 p_vout->p_sys->yuv_window,
571 ExposureMask, &xevent ) == True )
573 /* Window exposed (only handled if stream playback is paused) */
574 if( xevent.type == Expose )
576 if( ((XExposeEvent *)&xevent)->count == 0 )
577 /* (if this is the last a collection of expose events...) */
578 if( p_main->p_intf->p_input )
580 p_main->p_intf->p_input->stream.control.i_status )
581 XVideoDisplay( p_vout );
585 /* ClientMessage event - only WM_PROTOCOLS with WM_DELETE_WINDOW data
586 * are handled - according to the man pages, the format is always 32
588 while( XCheckTypedEvent( p_vout->p_sys->p_display,
589 ClientMessage, &xevent ) )
591 if( (xevent.xclient.message_type == p_vout->p_sys->wm_protocols)
592 && (xevent.xclient.data.l[0] == p_vout->p_sys->wm_delete_window ) )
594 p_main->p_intf->b_die = 1;
598 intf_DbgMsg( "%p -> unhandled ClientMessage received", p_vout );
602 if ( p_vout->i_changes & VOUT_FULLSCREEN_CHANGE )
604 intf_DbgMsg( "vout: changing full-screen status" );
606 p_vout->b_fullscreen = !p_vout->b_fullscreen;
608 /* Get rid of the old window */
609 XVideoDestroyWindow( p_vout );
611 /* And create a new one */
612 if( XVideoCreateWindow( p_vout ) )
614 intf_ErrMsg( "vout error: cannot create X11 window" );
615 XCloseDisplay( p_vout->p_sys->p_display );
617 free( p_vout->p_sys );
623 if( (p_vout->i_changes & VOUT_GRAYSCALE_CHANGE))
625 /* FIXME: clear flags ?? */
631 if( p_vout->i_changes & VOUT_SIZE_CHANGE )
633 intf_DbgMsg( "vout: resizing window" );
634 p_vout->i_changes &= ~VOUT_SIZE_CHANGE;
636 p_vout->i_width = p_vout->p_sys->i_window_width;
637 p_vout->i_height = p_vout->p_sys->i_window_height;
639 intf_WarnMsg( 3, "vout: video display resized (%dx%d)",
640 p_vout->i_width, p_vout->i_height );
643 /* Autohide Cursor */
644 if( p_vout->p_sys->b_mouse_pointer_visible &&
645 mdate() - p_vout->p_sys->i_time_mouse_last_moved > 2000000 )
647 X11ToggleMousePointer( p_vout );
653 /*****************************************************************************
654 * vout_Display: displays previously rendered output
655 *****************************************************************************
656 * This function sends the currently rendered image to X11 server.
657 * (The Xv extension takes care of "double-buffering".)
658 *****************************************************************************/
659 static void vout_Display( vout_thread_t *p_vout )
661 boolean_t b_draw = 1;
662 int i_size = p_vout->p_rendered_pic->i_width *
663 p_vout->p_rendered_pic->i_height;
665 if( XVideoUpdateImgSizeIfRequired( p_vout ) )
670 switch( p_vout->p_rendered_pic->i_type )
672 case YUV_422_PICTURE:
673 intf_ErrMsg( "vout error: YUV_422_PICTURE not (yet) supported" );
677 case YUV_444_PICTURE:
678 intf_ErrMsg( "vout error: YUV_444_PICTURE not (yet) supported" );
682 case YUV_420_PICTURE:
683 pf_fast_memcpy( p_vout->p_sys->p_xvimage->data,
684 p_vout->p_rendered_pic->p_y, i_size );
685 pf_fast_memcpy( p_vout->p_sys->p_xvimage->data + ( i_size ),
686 p_vout->p_rendered_pic->p_v, i_size / 4 );
687 pf_fast_memcpy( p_vout->p_sys->p_xvimage->data + ( i_size ) + ( i_size / 4 ),
688 p_vout->p_rendered_pic->p_u, i_size / 4 );
694 XVideoDisplay( p_vout );
698 static void vout_SetPalette( p_vout_thread_t p_vout,
699 u16 *red, u16 *green, u16 *blue, u16 *transp )
704 /* following functions are local */
706 /*****************************************************************************
707 * XVideoUpdateImgSizeIfRequired
708 *****************************************************************************
709 * This function checks to see if the image to be displayed is of a different
710 * size to the last image displayed. If so, the old shm block must be
711 * destroyed and a new one created.
712 * Note: the "image size" is the size of the image to be passed to the Xv
713 * extension (which is probably different to the size of the output window).
714 *****************************************************************************/
715 static int XVideoUpdateImgSizeIfRequired( vout_thread_t *p_vout )
717 int i_img_width = p_vout->p_rendered_pic->i_width;
718 int i_img_height = p_vout->p_rendered_pic->i_height;
720 if( p_vout->p_sys->i_image_width != i_img_width
721 || p_vout->p_sys->i_image_height != i_img_height )
723 if( p_vout->p_sys->i_image_width != 0
724 && p_vout->p_sys->i_image_height != 0 )
726 /* Destroy XvImage to change its size */
730 p_vout->p_sys->i_image_width = i_img_width;
731 p_vout->p_sys->i_image_height = i_img_height;
733 /* Create XvImage using XShm extension */
734 if( XVideoCreateShmImage( p_vout->p_sys->p_display,
735 p_vout->p_sys->xv_port,
736 &p_vout->p_sys->p_xvimage,
737 &p_vout->p_sys->shm_info,
738 i_img_width, i_img_height ) )
740 intf_ErrMsg( "vout: failed to create xvimage." );
741 p_vout->p_sys->i_image_width = 0;
745 /* Set bytes per line and initialize buffers */
746 p_vout->i_bytes_per_line =
747 (p_vout->p_sys->p_xvimage->data_size) /
748 (p_vout->p_sys->p_xvimage->height);
755 /*****************************************************************************
756 * XVideoCheckForXv: check for the XVideo extension
757 *****************************************************************************/
758 static int XVideoCheckForXv( Display *dpy )
762 switch( XvQueryExtension( dpy, &i, &i, &i, &i, &i ) )
768 intf_WarnMsg( 3, "vout error: XvBadExtension" );
772 intf_WarnMsg( 3, "vout error: XvBadAlloc" );
776 intf_WarnMsg( 3, "vout error: XvQueryExtension failed" );
781 /*****************************************************************************
782 * XVideoCreateWindow: open and set-up XVideo main window
783 *****************************************************************************/
784 static int XVideoCreateWindow( vout_thread_t *p_vout )
786 XSizeHints xsize_hints;
787 XSetWindowAttributes xwindow_attributes;
794 boolean_t b_configure_notify;
795 boolean_t b_map_notify;
798 /* Set main window's size */
799 /* If we're full screen, we're full screen! */
800 if( p_vout->b_fullscreen )
802 p_vout->p_sys->i_window_width = DisplayWidth( p_vout->p_sys->p_display,
803 p_vout->p_sys->i_screen );
804 p_vout->p_sys->i_window_height = DisplayHeight( p_vout->p_sys->p_display,
805 p_vout->p_sys->i_screen );
809 p_vout->p_sys->i_window_width = p_vout->i_width;
810 p_vout->p_sys->i_window_height = p_vout->i_height;
813 /* Prepare window manager hints and properties */
814 xsize_hints.base_width = p_vout->p_sys->i_window_width;
815 xsize_hints.base_height = p_vout->p_sys->i_window_height;
816 xsize_hints.flags = PSize;
817 p_vout->p_sys->wm_protocols = XInternAtom( p_vout->p_sys->p_display,
818 "WM_PROTOCOLS", True );
819 p_vout->p_sys->wm_delete_window = XInternAtom( p_vout->p_sys->p_display,
820 "WM_DELETE_WINDOW", True );
822 /* Prepare window attributes */
823 xwindow_attributes.background_pixel = BlackPixel( p_vout->p_sys->p_display,
824 p_vout->p_sys->i_screen );
826 xwindow_attributes.event_mask = ExposureMask | StructureNotifyMask;
828 /* Create the window and set hints - the window must receive ConfigureNotify
829 * events, and, until it is displayed, Expose and MapNotify events. */
830 p_vout->p_sys->window =
831 XCreateWindow( p_vout->p_sys->p_display,
832 DefaultRootWindow( p_vout->p_sys->p_display ),
834 p_vout->p_sys->i_window_width,
835 p_vout->p_sys->i_window_height, 1,
837 CWBackPixel | CWEventMask,
838 &xwindow_attributes );
840 if ( p_vout->b_fullscreen )
842 prop = XInternAtom(p_vout->p_sys->p_display, "_MOTIF_WM_HINTS", False);
843 mwmhints.flags = MWM_HINTS_DECORATIONS;
844 mwmhints.decorations = 0;
845 XChangeProperty( p_vout->p_sys->p_display, p_vout->p_sys->window,
846 prop, prop, 32, PropModeReplace,
847 (unsigned char *)&mwmhints, PROP_MWM_HINTS_ELEMENTS );
849 XSetTransientForHint( p_vout->p_sys->p_display,
850 p_vout->p_sys->window, None );
851 XRaiseWindow( p_vout->p_sys->p_display, p_vout->p_sys->window );
855 /* Set window manager hints and properties: size hints, command,
856 * window's name, and accepted protocols */
857 XSetWMNormalHints( p_vout->p_sys->p_display, p_vout->p_sys->window,
859 XSetCommand( p_vout->p_sys->p_display, p_vout->p_sys->window,
860 p_main->ppsz_argv, p_main->i_argc );
861 XStoreName( p_vout->p_sys->p_display, p_vout->p_sys->window,
862 VOUT_TITLE " (XVideo output)" );
864 if( (p_vout->p_sys->wm_protocols == None) /* use WM_DELETE_WINDOW */
865 || (p_vout->p_sys->wm_delete_window == None)
866 || !XSetWMProtocols( p_vout->p_sys->p_display, p_vout->p_sys->window,
867 &p_vout->p_sys->wm_delete_window, 1 ) )
869 /* WM_DELETE_WINDOW is not supported by window manager */
870 intf_Msg( "vout error: missing or bad window manager" );
873 /* Creation of a graphic context that doesn't generate a GraphicsExpose
874 * event when using functions like XCopyArea */
875 xgcvalues.graphics_exposures = False;
876 p_vout->p_sys->gc = XCreateGC( p_vout->p_sys->p_display,
877 p_vout->p_sys->window,
878 GCGraphicsExposures, &xgcvalues);
880 /* Send orders to server, and wait until window is displayed - three
881 * events must be received: a MapNotify event, an Expose event allowing
882 * drawing in the window, and a ConfigureNotify to get the window
883 * dimensions. Once those events have been received, only ConfigureNotify
884 * events need to be received. */
886 b_configure_notify = 0;
888 XMapWindow( p_vout->p_sys->p_display, p_vout->p_sys->window);
891 XNextEvent( p_vout->p_sys->p_display, &xevent);
892 if( (xevent.type == Expose)
893 && (xevent.xexpose.window == p_vout->p_sys->window) )
897 else if( (xevent.type == MapNotify)
898 && (xevent.xmap.window == p_vout->p_sys->window) )
902 else if( (xevent.type == ConfigureNotify)
903 && (xevent.xconfigure.window == p_vout->p_sys->window) )
905 b_configure_notify = 1;
906 p_vout->p_sys->i_window_width = xevent.xconfigure.width;
907 p_vout->p_sys->i_window_height = xevent.xconfigure.height;
909 } while( !( b_expose && b_configure_notify && b_map_notify ) );
911 XSelectInput( p_vout->p_sys->p_display, p_vout->p_sys->window,
912 StructureNotifyMask | KeyPressMask |
913 ButtonPressMask | ButtonReleaseMask |
916 if( p_vout->b_fullscreen )
918 XSetInputFocus( p_vout->p_sys->p_display, p_vout->p_sys->window,
919 RevertToNone, CurrentTime );
920 XMoveWindow( p_vout->p_sys->p_display, p_vout->p_sys->window, 0, 0 );
923 /* Create YUV output sub-window. */
924 p_vout->p_sys->yuv_window=XCreateSimpleWindow( p_vout->p_sys->p_display,
925 p_vout->p_sys->window, 0, 0, 1, 1, 0,
926 BlackPixel( p_vout->p_sys->p_display,
927 p_vout->p_sys->i_screen ),
928 WhitePixel( p_vout->p_sys->p_display,
929 p_vout->p_sys->i_screen ) );
931 p_vout->p_sys->yuv_gc = XCreateGC( p_vout->p_sys->p_display,
932 p_vout->p_sys->yuv_window,
933 GCGraphicsExposures, &xgcvalues );
935 XSetWindowBackground( p_vout->p_sys->p_display, p_vout->p_sys->yuv_window,
936 BlackPixel(p_vout->p_sys->p_display, p_vout->p_sys->i_screen ) );
938 XMapWindow( p_vout->p_sys->p_display, p_vout->p_sys->yuv_window );
939 XSelectInput( p_vout->p_sys->p_display, p_vout->p_sys->yuv_window,
942 /* If the cursor was formerly blank than blank it again */
943 if( !p_vout->p_sys->b_mouse_pointer_visible )
945 X11ToggleMousePointer( p_vout );
946 X11ToggleMousePointer( p_vout );
949 XSync( p_vout->p_sys->p_display, False );
954 static void XVideoDestroyWindow( vout_thread_t *p_vout )
956 XSync( p_vout->p_sys->p_display, False );
958 XFreeGC( p_vout->p_sys->p_display, p_vout->p_sys->yuv_gc );
959 XDestroyWindow( p_vout->p_sys->p_display, p_vout->p_sys->yuv_window );
961 XUnmapWindow( p_vout->p_sys->p_display, p_vout->p_sys->window );
962 XFreeGC( p_vout->p_sys->p_display, p_vout->p_sys->gc );
963 XDestroyWindow( p_vout->p_sys->p_display, p_vout->p_sys->window );
966 /*****************************************************************************
967 * XVideoCreateShmImage: create an XvImage using shared memory extension
968 *****************************************************************************
969 * Prepare an XvImage for display function.
970 * The order of the operations respects the recommandations of the mit-shm
971 * document by J.Corbet and K.Packard. Most of the parameters were copied from
973 *****************************************************************************/
974 static int XVideoCreateShmImage( Display* dpy, int xv_port,
975 XvImage **pp_xvimage,
976 XShmSegmentInfo *p_shm_info,
977 int i_width, int i_height )
979 *pp_xvimage = XvShmCreateImage( dpy, xv_port,
980 GUID_YUV12_PLANAR, 0,
985 intf_ErrMsg( "vout error: XvShmCreateImage failed." );
989 p_shm_info->shmid = shmget( IPC_PRIVATE, (*pp_xvimage)->data_size,
991 if( p_shm_info->shmid < 0) /* error */
993 intf_ErrMsg( "vout error: cannot allocate shared image data (%s)",
998 p_shm_info->shmaddr = (*pp_xvimage)->data = shmat( p_shm_info->shmid,
1000 p_shm_info->readOnly = False;
1002 if( !XShmAttach( dpy, p_shm_info ) )
1004 intf_ErrMsg( "vout error: XShmAttach failed" );
1005 shmctl( p_shm_info->shmid, IPC_RMID, 0 );
1006 shmdt( p_shm_info->shmaddr );
1010 /* Send image to X server. This instruction is required, since having
1011 * built a Shm XImage and not using it causes an error on XCloseDisplay */
1012 XSync( dpy, False );
1014 /* Mark the shm segment to be removed when there will be no more
1015 * attachements, so it is automatic on process exit or after shmdt */
1016 shmctl( p_shm_info->shmid, IPC_RMID, 0 );
1021 /*****************************************************************************
1022 * XVideoDestroyShmImage
1023 *****************************************************************************
1024 * Destroy XImage AND associated data. Detach shared memory segment from
1025 * server and process, then free it. If pointer is NULL, the image won't be
1026 * destroyed (see vout_ManageOutputMethod())
1027 *****************************************************************************/
1028 static void XVideoDestroyShmImage( vout_thread_t *p_vout, XvImage *p_xvimage,
1029 XShmSegmentInfo *p_shm_info )
1031 /* If pointer is NULL, do nothing */
1032 if( p_xvimage == NULL )
1037 XSync( p_vout->p_sys->p_display, False );
1038 XShmDetach( p_vout->p_sys->p_display, p_shm_info );/* detach from server */
1040 XDestroyImage( p_ximage ); /* XXX */
1044 if( shmdt( p_shm_info->shmaddr ) ) /* detach shared memory from process */
1046 intf_ErrMsg( "vout error: cannot detach shared memory (%s)",
1051 /*****************************************************************************
1052 * XVideoEnableScreenSaver: enable screen saver
1053 *****************************************************************************
1054 * This function enable the screen saver on a display after it had been
1055 * disabled by XDisableScreenSaver. Both functions use a counter mechanism to
1056 * know wether the screen saver can be activated or not: if n successive calls
1057 * are made to XDisableScreenSaver, n successive calls to XEnableScreenSaver
1058 * will be required before the screen saver could effectively be activated.
1059 *****************************************************************************/
1060 void XVideoEnableScreenSaver( vout_thread_t *p_vout )
1062 intf_DbgMsg( "intf: enabling screen saver" );
1063 XSetScreenSaver( p_vout->p_sys->p_display, p_vout->p_sys->i_ss_timeout,
1064 p_vout->p_sys->i_ss_interval,
1065 p_vout->p_sys->i_ss_blanking,
1066 p_vout->p_sys->i_ss_exposure );
1068 DPMSEnable( p_vout->p_sys->p_display );
1071 /*****************************************************************************
1072 * XVideoDisableScreenSaver: disable screen saver
1073 *****************************************************************************
1074 * See XEnableScreenSaver
1075 *****************************************************************************/
1076 void XVideoDisableScreenSaver( vout_thread_t *p_vout )
1078 /* Save screen saver informations */
1079 XGetScreenSaver( p_vout->p_sys->p_display, &p_vout->p_sys->i_ss_timeout,
1080 &p_vout->p_sys->i_ss_interval,
1081 &p_vout->p_sys->i_ss_blanking,
1082 &p_vout->p_sys->i_ss_exposure );
1084 /* Disable screen saver */
1085 intf_DbgMsg( "intf: disabling screen saver" );
1086 XSetScreenSaver( p_vout->p_sys->p_display, 0,
1087 p_vout->p_sys->i_ss_interval,
1088 p_vout->p_sys->i_ss_blanking,
1089 p_vout->p_sys->i_ss_exposure );
1091 DPMSDisable( p_vout->p_sys->p_display );
1094 /*****************************************************************************
1095 * X11ToggleMousePointer: hide or show the mouse pointer
1096 *****************************************************************************
1097 * This function hides the X pointer if requested.
1098 *****************************************************************************/
1099 void X11ToggleMousePointer( vout_thread_t *p_vout )
1102 if( p_vout->p_sys->b_mouse_pointer_visible )
1104 XDefineCursor( p_vout->p_sys->p_display,
1105 p_vout->p_sys->window,
1106 p_vout->p_sys->blank_cursor );
1107 p_vout->p_sys->b_mouse_pointer_visible = 0;
1111 XUndefineCursor( p_vout->p_sys->p_display, p_vout->p_sys->window );
1112 p_vout->p_sys->b_mouse_pointer_visible = 1;
1116 /* This based on some code in SetBufferPicture... At the moment it's only
1117 * used by the xvideo plugin, but others may want to use it. */
1118 static void XVideoOutputCoords( const picture_t *p_pic, const boolean_t scale,
1119 const int win_w, const int win_h,
1120 int *dx, int *dy, int *w, int *h )
1125 /*****************************************************************************
1126 * XVideoGetPort: get YUV12 port
1127 *****************************************************************************
1129 *****************************************************************************/
1130 static int XVideoGetPort( Display *dpy )
1132 XvAdaptorInfo *p_adaptor;
1133 int i_adaptor, i_num_adaptors, i_requested_adaptor;
1134 int i_selected_port;
1136 switch( XvQueryAdaptors( dpy, DefaultRootWindow( dpy ),
1137 &i_num_adaptors, &p_adaptor ) )
1142 case XvBadExtension:
1143 intf_WarnMsg( 3, "vout error: XvBadExtension for XvQueryAdaptors" );
1147 intf_WarnMsg( 3, "vout error: XvBadAlloc for XvQueryAdaptors" );
1151 intf_WarnMsg( 3, "vout error: XvQueryAdaptors failed" );
1155 i_selected_port = -1;
1156 i_requested_adaptor = main_GetIntVariable( VOUT_XVADAPTOR_VAR, -1 );
1158 /* No special xv port has been requested so try all of them */
1159 for( i_adaptor = 0; i_adaptor < i_num_adaptors; ++i_adaptor )
1163 /* If we requested an adaptor and it's not this one, we aren't
1165 if( i_requested_adaptor != -1 && i_adaptor != i_requested_adaptor )
1170 /* If the adaptor doesn't have the required properties, skip it */
1171 if( !( p_adaptor[ i_adaptor ].type & XvInputMask ) ||
1172 !( p_adaptor[ i_adaptor ].type & XvImageMask ) )
1177 for( i_port = p_adaptor[i_adaptor].base_id;
1178 i_port < p_adaptor[i_adaptor].base_id
1179 + p_adaptor[i_adaptor].num_ports;
1182 XvImageFormatValues *p_formats;
1183 int i_format, i_num_formats;
1185 /* If we already found a port, we aren't interested */
1186 if( i_selected_port != -1 )
1191 /* Check that port supports YUV12 planar format... */
1192 p_formats = XvListImageFormats( dpy, i_port, &i_num_formats );
1194 for( i_format = 0; i_format < i_num_formats; i_format++ )
1196 XvEncodingInfo *p_enc;
1197 int i_enc, i_num_encodings;
1198 XvAttribute *p_attr;
1199 int i_attr, i_num_attributes;
1201 if( p_formats[ i_format ].id != GUID_YUV12_PLANAR )
1206 /* Found a matching port, print a description of this port */
1207 i_selected_port = i_port;
1209 intf_WarnMsg( 3, "vout: XVideoGetPort found adaptor %i port %i",
1211 intf_WarnMsg( 3, " image format 0x%x (%4.4s) %s supported",
1212 p_formats[ i_format ].id,
1213 (char *)&p_formats[ i_format ].id,
1214 ( p_formats[ i_format ].format
1215 == XvPacked ) ? "packed" : "planar" );
1217 intf_WarnMsg( 4, " encoding list:" );
1219 if( XvQueryEncodings( dpy, i_port, &i_num_encodings, &p_enc )
1222 intf_WarnMsg( 4, " XvQueryEncodings failed" );
1226 for( i_enc = 0; i_enc < i_num_encodings; i_enc++ )
1228 intf_WarnMsg( 4, " id=%ld, name=%s, size=%ldx%ld,"
1229 " numerator=%d, denominator=%d",
1230 p_enc[i_enc].encoding_id, p_enc[i_enc].name,
1231 p_enc[i_enc].width, p_enc[i_enc].height,
1232 p_enc[i_enc].rate.numerator,
1233 p_enc[i_enc].rate.denominator );
1238 XvFreeEncodingInfo( p_enc );
1241 intf_WarnMsg( 4, " attribute list:" );
1242 p_attr = XvQueryPortAttributes( dpy, i_port,
1243 &i_num_attributes );
1244 for( i_attr = 0; i_attr < i_num_attributes; i_attr++ )
1247 " name=%s, flags=[%s%s ], min=%i, max=%i",
1248 p_attr[i_attr].name,
1249 (p_attr[i_attr].flags & XvGettable) ? " get" : "",
1250 (p_attr[i_attr].flags & XvSettable) ? " set" : "",
1251 p_attr[i_attr].min_value, p_attr[i_attr].max_value );
1254 if( p_attr != NULL )
1260 if( p_formats != NULL )
1267 if( i_num_adaptors > 0 )
1269 XvFreeAdaptorInfo( p_adaptor );
1272 if( i_selected_port == -1 )
1274 if( i_requested_adaptor == -1 )
1276 intf_WarnMsg( 3, "vout: no XVideo port found supporting YUV12" );
1280 intf_WarnMsg( 3, "vout: XVideo adaptor %i does not support YUV12",
1281 i_requested_adaptor );
1285 return( i_selected_port );
1289 /*****************************************************************************
1290 * XVideoDisplay: display image
1291 *****************************************************************************
1292 * This function displays the image stored in p_vout->p_sys->p_xvimage.
1293 * The image is scaled to fit in the output window (and to have the correct
1295 *****************************************************************************/
1296 static void XVideoDisplay( vout_thread_t *p_vout )
1298 int i_dest_width, i_dest_height;
1299 int i_dest_x, i_dest_y;
1301 if( !p_vout->p_sys->p_xvimage || !p_vout->p_rendered_pic )
1306 i_dest_height = p_vout->p_sys->i_window_height >
1307 p_vout->p_rendered_pic->i_height
1308 ? p_vout->p_sys->i_window_height
1309 : p_vout->p_rendered_pic->i_height;
1310 i_dest_width = p_vout->p_sys->i_window_width >
1311 p_vout->p_rendered_pic->i_width
1312 ? p_vout->p_sys->i_window_width
1313 : p_vout->p_rendered_pic->i_width;
1315 if( p_vout->b_scale )
1317 int i_ratio = 900 * i_dest_width / i_dest_height;
1319 switch( p_vout->p_rendered_pic->i_aspect_ratio )
1321 case AR_3_4_PICTURE:
1322 if( i_ratio < 1200 )
1324 i_dest_width = i_dest_height * 4 / 3;
1328 i_dest_height = i_dest_width * 3 / 4;
1333 case AR_16_9_PICTURE:
1334 if( i_ratio < 1600 )
1336 i_dest_width = i_dest_height * 16 / 9;
1340 i_dest_height = i_dest_width * 9 / 16;
1345 case AR_221_1_PICTURE:
1346 if( i_ratio < 1989 )
1348 i_dest_width = i_dest_height * 221 / 100;
1352 i_dest_height = i_dest_width * 100 / 221;
1357 case AR_SQUARE_PICTURE:
1361 i_dest_width = i_dest_height * p_vout->p_rendered_pic->i_width / p_vout->p_rendered_pic->i_height;
1365 i_dest_height = i_dest_width * p_vout->p_rendered_pic->i_height / p_vout->p_rendered_pic->i_width;
1372 DisplayWidth( p_vout->p_sys->p_display, p_vout->p_sys->i_screen ) )
1374 i_dest_width = DisplayWidth( p_vout->p_sys->p_display,
1375 p_vout->p_sys->i_screen );
1376 i_dest_height = 900 * i_dest_width / i_ratio;
1378 else if( i_dest_height >
1379 DisplayHeight( p_vout->p_sys->p_display, p_vout->p_sys->i_screen ) )
1381 i_dest_height = DisplayHeight( p_vout->p_sys->p_display,
1382 p_vout->p_sys->i_screen );
1383 i_dest_width = i_ratio * i_dest_height / 900;
1387 XvShmPutImage( p_vout->p_sys->p_display, p_vout->p_sys->xv_port,
1388 p_vout->p_sys->yuv_window, p_vout->p_sys->gc,
1389 p_vout->p_sys->p_xvimage,
1390 0 /*src_x*/, 0 /*src_y*/,
1391 p_vout->p_rendered_pic->i_width,
1392 p_vout->p_rendered_pic->i_height,
1393 0 /*dest_x*/, 0 /*dest_y*/, i_dest_width, i_dest_height,
1397 XResizeWindow( p_vout->p_sys->p_display, p_vout->p_sys->yuv_window,
1398 i_dest_width, i_dest_height );
1401 if( ( ( i_dest_width != p_vout->p_sys->i_window_width ) ||
1402 ( i_dest_height != p_vout->p_sys->i_window_height ) ) &&
1403 ! p_vout->b_fullscreen )
1405 p_vout->p_sys->i_window_width = i_dest_width;
1406 p_vout->p_sys->i_window_height = i_dest_height;
1407 // p_vout->i_changes |= VOUT_SIZE_CHANGE;
1408 XResizeWindow( p_vout->p_sys->p_display, p_vout->p_sys->window,
1409 i_dest_width, i_dest_height );
1412 /* Set picture position */
1413 i_dest_x = (p_vout->p_sys->i_window_width - i_dest_width) / 2;
1414 i_dest_y = (p_vout->p_sys->i_window_height - i_dest_height) / 2;
1416 XMoveWindow( p_vout->p_sys->p_display, p_vout->p_sys->yuv_window,
1417 i_dest_x, i_dest_y );
1419 /* Send the order to the X server */
1420 XSync( p_vout->p_sys->p_display, False );
1424 /*****************************************************************************
1425 * XVideoSetAttribute
1426 *****************************************************************************
1427 * This function can be used to set attributes, e.g. XV_BRIGHTNESS and
1428 * XV_CONTRAST. "f_value" should be in the range of 0 to 1.
1429 *****************************************************************************/
1430 static void XVideoSetAttribute( vout_thread_t *p_vout,
1431 char *attr_name, float f_value )
1434 XvAttribute *p_attrib;
1435 Display *p_dpy = p_vout->p_sys->p_display;
1436 int xv_port = p_vout->p_sys->xv_port;
1438 p_attrib = XvQueryPortAttributes( p_dpy, xv_port, &i_attrib );
1444 if( i_attrib >= 0 && !strcmp( p_attrib[ i_attrib ].name, attr_name ) )
1446 int i_sv = f_value * ( p_attrib[ i_attrib ].max_value
1447 - p_attrib[ i_attrib ].min_value + 1 )
1448 + p_attrib[ i_attrib ].min_value;
1450 XvSetPortAttribute( p_dpy, xv_port,
1451 XInternAtom( p_dpy, attr_name, False ), i_sv );
1455 } while( i_attrib > 0 );