1 /*****************************************************************************
2 * vout_common.c: Functions common to the X11 and XVideo plugins
3 *****************************************************************************
4 * Copyright (C) 1998-2001 VideoLAN
5 * $Id: vout_common.c,v 1.7 2001/12/20 15:43:15 sam Exp $
7 * Authors: Vincent Seguin <seguin@via.ecp.fr>
8 * Samuel Hocevar <sam@zoy.org>
9 * David Kennedy <dkennedy@tinytoad.com>
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.
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.
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 *****************************************************************************/
26 #include "modules_inner.h"
28 /*****************************************************************************
30 *****************************************************************************/
33 #include <errno.h> /* ENOMEM */
34 #include <stdlib.h> /* free() */
35 #include <string.h> /* strerror() */
37 #ifdef HAVE_MACHINE_PARAM_H
39 #include <machine/param.h>
40 #include <sys/types.h> /* typedef ushort */
45 #include <netinet/in.h> /* BSD: struct in_addr */
48 #include <sys/shm.h> /* shmget(), shmctl() */
50 #include <X11/Xutil.h>
51 #include <X11/keysym.h>
52 #include <X11/extensions/XShm.h>
56 #if ( MODULE_NAME == x11 )
57 # define MODULE_NAME_IS_x11 1
58 #elif ( MODULE_NAME == xvideo )
59 # define MODULE_NAME_IS_xvideo 1
60 # include <X11/extensions/Xv.h>
61 # include <X11/extensions/Xvlib.h>
62 # include <X11/extensions/dpms.h>
74 #include "video_output.h"
75 #include "vout_common.h"
77 #include "interface.h"
78 #include "netutils.h" /* network_ChannelJoin */
80 #include "stream_control.h" /* needed by input_ext-intf.h... */
81 #include "input_ext-intf.h"
84 #include "modules_export.h"
86 static __inline__ void vout_Seek( off_t i_seek )
88 #define area p_main->p_intf->p_input->stream.p_selected_area
89 off_t i_tell = area->i_tell;
91 i_tell += i_seek * (off_t)50 * p_main->p_intf->p_input->stream.i_mux_rate;
93 i_tell = ( i_tell <= area->i_start ) ? area->i_start
94 : ( i_tell >= area->i_size ) ? area->i_size
97 input_Seek( p_main->p_intf->p_input, i_tell );
101 /*****************************************************************************
102 * vout_Manage: handle X11 events
103 *****************************************************************************
104 * This function should be called regularly by video output thread. It manages
105 * X11 events and allows window resizing. It returns a non null value on
107 *****************************************************************************/
108 int _M( vout_Manage ) ( vout_thread_t *p_vout )
110 XEvent xevent; /* X11 event */
111 boolean_t b_resized; /* window has been resized */
112 char i_key; /* ISO Latin-1 key */
115 /* Handle X11 events: ConfigureNotify events are parsed to know if the
116 * output window's size changed, MapNotify and UnmapNotify to know if the
117 * window is mapped (and if the display is useful), and ClientMessages
118 * to intercept window destruction requests */
121 while( XCheckWindowEvent( p_vout->p_sys->p_display, p_vout->p_sys->window,
122 StructureNotifyMask | KeyPressMask |
123 ButtonPressMask | ButtonReleaseMask |
124 PointerMotionMask | Button1MotionMask , &xevent )
127 /* ConfigureNotify event: prepare */
128 if( (xevent.type == ConfigureNotify)
129 && ((xevent.xconfigure.width != p_vout->p_sys->i_width)
130 || (xevent.xconfigure.height != p_vout->p_sys->i_height)) )
132 /* Update dimensions */
134 p_vout->p_sys->i_width = xevent.xconfigure.width;
135 p_vout->p_sys->i_height = xevent.xconfigure.height;
137 /* MapNotify event: change window status and disable screen saver */
138 else if( xevent.type == MapNotify)
140 if( (p_vout != NULL) && !p_vout->b_active )
142 _M( XCommonDisableScreenSaver ) ( p_vout );
143 p_vout->b_active = 1;
146 /* UnmapNotify event: change window status and enable screen saver */
147 else if( xevent.type == UnmapNotify )
149 if( (p_vout != NULL) && p_vout->b_active )
151 _M( XCommonEnableScreenSaver ) ( p_vout );
152 p_vout->b_active = 0;
156 else if( xevent.type == KeyPress )
158 /* We may have keys like F1 trough F12, ESC ... */
159 x_key_symbol = XKeycodeToKeysym( p_vout->p_sys->p_display,
160 xevent.xkey.keycode, 0 );
161 switch( x_key_symbol )
164 p_main->p_intf->b_die = 1;
167 p_main->p_intf->b_menu_change = 1;
182 input_Seek( p_main->p_intf->p_input,
183 p_main->p_intf->p_input->stream.p_selected_area->i_start );
186 input_Seek( p_main->p_intf->p_input,
187 p_main->p_intf->p_input->stream.p_selected_area->i_size );
196 input_SetStatus( p_main->p_intf->p_input,
197 INPUT_STATUS_PAUSE );
202 * The reason why I use this instead of XK_0 is that
203 * with XLookupString, we don't have to care about
206 if( XLookupString( &xevent.xkey, &i_key, 1, NULL, NULL ) )
208 /* FIXME: handle stuff here */
213 p_main->p_intf->b_die = 1;
217 p_vout->i_changes |= VOUT_FULLSCREEN_CHANGE;
220 case '0': network_ChannelJoin( 0 ); break;
221 case '1': network_ChannelJoin( 1 ); break;
222 case '2': network_ChannelJoin( 2 ); break;
223 case '3': network_ChannelJoin( 3 ); break;
224 case '4': network_ChannelJoin( 4 ); break;
225 case '5': network_ChannelJoin( 5 ); break;
226 case '6': network_ChannelJoin( 6 ); break;
227 case '7': network_ChannelJoin( 7 ); break;
228 case '8': network_ChannelJoin( 8 ); break;
229 case '9': network_ChannelJoin( 9 ); break;
232 intf_DbgMsg( "vout: unhandled key '%c' (%i)",
233 (char)i_key, i_key );
241 else if( xevent.type == ButtonPress )
243 switch( ((XButtonEvent *)&xevent)->button )
246 /* In this part we will eventually manage
247 * clicks for DVD navigation for instance. For the
248 * moment just pause the stream. */
249 input_SetStatus( p_main->p_intf->p_input,
250 INPUT_STATUS_PAUSE );
263 else if( xevent.type == ButtonRelease )
265 switch( ((XButtonEvent *)&xevent)->button )
268 /* FIXME: need locking ! */
269 p_main->p_intf->b_menu_change = 1;
274 else if( xevent.type == MotionNotify )
276 p_vout->p_sys->i_time_mouse_last_moved = mdate();
277 if( ! p_vout->p_sys->b_mouse_pointer_visible )
279 _M( XCommonToggleMousePointer ) ( p_vout );
285 intf_WarnMsg( 3, "vout: unhandled event %d received", xevent.type );
289 /* Handle events for YUV video output sub-window */
290 while( XCheckWindowEvent( p_vout->p_sys->p_display,
291 p_vout->p_sys->yuv_window,
292 ExposureMask, &xevent ) == True )
294 /* Window exposed (only handled if stream playback is paused) */
295 if( xevent.type == Expose )
297 if( ((XExposeEvent *)&xevent)->count == 0 )
299 /* (if this is the last a collection of expose events...) */
300 if( p_main->p_intf->p_input != NULL )
303 p_main->p_intf->p_input->stream.control.i_status )
305 /* XVideoDisplay( p_vout )*/;
312 /* ClientMessage event - only WM_PROTOCOLS with WM_DELETE_WINDOW data
313 * are handled - according to the man pages, the format is always 32
315 while( XCheckTypedEvent( p_vout->p_sys->p_display,
316 ClientMessage, &xevent ) )
318 if( (xevent.xclient.message_type == p_vout->p_sys->wm_protocols)
319 && (xevent.xclient.data.l[0] == p_vout->p_sys->wm_delete_window ) )
321 p_main->p_intf->b_die = 1;
325 intf_DbgMsg( "vout: unhandled ClientMessage received" );
329 if ( p_vout->i_changes & VOUT_FULLSCREEN_CHANGE )
331 p_vout->i_changes &= ~VOUT_FULLSCREEN_CHANGE;
333 p_vout->b_fullscreen = !p_vout->b_fullscreen;
335 /* Get rid of the old window */
336 _M( XCommonDestroyWindow ) ( p_vout );
338 /* And create a new one */
339 if( _M( XCommonCreateWindow ) ( p_vout ) )
341 intf_ErrMsg( "vout error: cannot create X11 window" );
342 XCloseDisplay( p_vout->p_sys->p_display );
344 free( p_vout->p_sys );
350 #ifdef MODULE_NAME_IS_x11
352 * Handle vout window resizing
356 /* If interface window has been resized, change vout size */
357 intf_DbgMsg( "vout: resizing output window" );
358 p_vout->i_width = p_vout->p_sys->i_width;
359 p_vout->i_height = p_vout->p_sys->i_height;
360 p_vout->i_changes |= VOUT_SIZE_CHANGE;
362 else if( (p_vout->i_width != p_vout->p_sys->i_width) ||
363 (p_vout->i_height != p_vout->p_sys->i_height) )
365 /* If video output size has changed, change interface window size */
366 intf_DbgMsg( "vout: resizing output window" );
367 p_vout->p_sys->i_width = p_vout->i_width;
368 p_vout->p_sys->i_height = p_vout->i_height;
369 XResizeWindow( p_vout->p_sys->p_display, p_vout->p_sys->window,
370 p_vout->p_sys->i_width, p_vout->p_sys->i_height );
373 * Color/Grayscale or gamma change: in 8bpp, just change the colormap
375 if( (p_vout->i_changes & VOUT_GRAYSCALE_CHANGE)
376 && (p_vout->i_screen_depth == 8) )
378 /* FIXME: clear flags ?? */
384 if( p_vout->i_changes & VOUT_SIZE_CHANGE )
386 intf_DbgMsg( "vout info: resizing window" );
387 p_vout->i_changes &= ~VOUT_SIZE_CHANGE;
390 XResizeWindow( p_vout->p_sys->p_display, p_vout->p_sys->window,
391 p_vout->i_width, p_vout->i_height );
393 /* Destroy XImages to change their size */
396 /* Recreate XImages. If SysInit failed, the thread can't go on. */
397 if( vout_Init( p_vout ) )
399 intf_ErrMsg( "vout error: cannot resize display" );
403 /* Tell the video output thread that it will need to rebuild YUV
404 * tables. This is needed since conversion buffer size may have
406 p_vout->i_changes |= VOUT_YUV_CHANGE;
407 intf_Msg( "vout: video display resized (%dx%d)",
408 p_vout->i_width, p_vout->i_height);
414 if( p_vout->i_changes & VOUT_SIZE_CHANGE )
416 p_vout->i_changes &= ~VOUT_SIZE_CHANGE;
418 intf_WarnMsg( 3, "vout: video display resized (%dx%d)",
419 p_vout->p_sys->i_width,
420 p_vout->p_sys->i_height );
424 /* Autohide Cursour */
425 if( mdate() - p_vout->p_sys->i_time_mouse_last_moved > 2000000 )
427 /* Hide the mouse automatically */
428 if( p_vout->p_sys->b_mouse_pointer_visible )
430 _M( XCommonToggleMousePointer ) ( p_vout );
437 /*****************************************************************************
438 * XCommonCreateWindow: open and set-up X11 main window
439 *****************************************************************************/
440 int _M( XCommonCreateWindow ) ( vout_thread_t *p_vout )
442 XSizeHints xsize_hints;
443 XSetWindowAttributes xwindow_attributes;
450 boolean_t b_configure_notify;
451 boolean_t b_map_notify;
453 /* If we're full screen, we're full screen! */
454 if( p_vout->b_fullscreen )
456 p_vout->p_sys->i_width =
457 DisplayWidth( p_vout->p_sys->p_display, p_vout->p_sys->i_screen );
458 p_vout->p_sys->i_height =
459 DisplayHeight( p_vout->p_sys->p_display, p_vout->p_sys->i_screen );
463 /* Set main window's size */
464 if( p_vout->render.i_height * p_vout->render.i_aspect
465 >= p_vout->render.i_width * VOUT_ASPECT_FACTOR )
467 p_vout->p_sys->i_width = p_vout->render.i_height
468 * p_vout->render.i_aspect / VOUT_ASPECT_FACTOR;
469 p_vout->p_sys->i_height = p_vout->render.i_height;
473 p_vout->p_sys->i_width = p_vout->render.i_width;
474 p_vout->p_sys->i_height = p_vout->render.i_width
475 * VOUT_ASPECT_FACTOR / p_vout->render.i_aspect;
478 if( p_vout->p_sys->i_width <= 300 && p_vout->p_sys->i_height <= 300 )
480 p_vout->p_sys->i_width <<= 1;
481 p_vout->p_sys->i_height <<= 1;
483 else if( p_vout->p_sys->i_width <= 400
484 && p_vout->p_sys->i_height <= 400 )
486 p_vout->p_sys->i_width += p_vout->p_sys->i_width >> 1;
487 p_vout->p_sys->i_height += p_vout->p_sys->i_height >> 1;
491 /* Prepare window manager hints and properties */
492 xsize_hints.base_width = p_vout->p_sys->i_width;
493 xsize_hints.base_height = p_vout->p_sys->i_height;
494 xsize_hints.flags = PSize;
495 p_vout->p_sys->wm_protocols = XInternAtom( p_vout->p_sys->p_display,
496 "WM_PROTOCOLS", True );
497 p_vout->p_sys->wm_delete_window = XInternAtom( p_vout->p_sys->p_display,
498 "WM_DELETE_WINDOW", True );
500 /* Prepare window attributes */
501 xwindow_attributes.backing_store = Always; /* save the hidden part */
502 xwindow_attributes.background_pixel = BlackPixel( p_vout->p_sys->p_display,
503 p_vout->p_sys->i_screen );
504 xwindow_attributes.event_mask = ExposureMask | StructureNotifyMask;
507 /* Create the window and set hints - the window must receive ConfigureNotify
508 * events, and, until it is displayed, Expose and MapNotify events. */
510 p_vout->p_sys->window =
511 XCreateWindow( p_vout->p_sys->p_display,
512 DefaultRootWindow( p_vout->p_sys->p_display ),
514 p_vout->p_sys->i_width,
515 p_vout->p_sys->i_height,
516 #ifdef MODULE_NAME_IS_x11
517 /* XXX - what's this ? */
523 CWBackingStore | CWBackPixel | CWEventMask,
524 &xwindow_attributes );
526 if ( p_vout->b_fullscreen )
528 prop = XInternAtom(p_vout->p_sys->p_display, "_MOTIF_WM_HINTS", False);
529 mwmhints.flags = MWM_HINTS_DECORATIONS;
530 mwmhints.decorations = 0;
531 XChangeProperty( p_vout->p_sys->p_display, p_vout->p_sys->window,
532 prop, prop, 32, PropModeReplace,
533 (unsigned char *)&mwmhints, PROP_MWM_HINTS_ELEMENTS );
535 XSetTransientForHint( p_vout->p_sys->p_display,
536 p_vout->p_sys->window, None );
537 XRaiseWindow( p_vout->p_sys->p_display, p_vout->p_sys->window );
540 /* Set window manager hints and properties: size hints, command,
541 * window's name, and accepted protocols */
542 XSetWMNormalHints( p_vout->p_sys->p_display, p_vout->p_sys->window,
544 XSetCommand( p_vout->p_sys->p_display, p_vout->p_sys->window,
545 p_main->ppsz_argv, p_main->i_argc );
546 XStoreName( p_vout->p_sys->p_display, p_vout->p_sys->window,
547 #ifdef MODULE_NAME_IS_x11
548 VOUT_TITLE " (X11 output)"
550 VOUT_TITLE " (XVideo output)"
554 if( (p_vout->p_sys->wm_protocols == None) /* use WM_DELETE_WINDOW */
555 || (p_vout->p_sys->wm_delete_window == None)
556 || !XSetWMProtocols( p_vout->p_sys->p_display, p_vout->p_sys->window,
557 &p_vout->p_sys->wm_delete_window, 1 ) )
559 /* WM_DELETE_WINDOW is not supported by window manager */
560 intf_Msg( "vout error: missing or bad window manager" );
563 /* Creation of a graphic context that doesn't generate a GraphicsExpose
564 * event when using functions like XCopyArea */
565 xgcvalues.graphics_exposures = False;
566 p_vout->p_sys->gc = XCreateGC( p_vout->p_sys->p_display,
567 p_vout->p_sys->window,
568 GCGraphicsExposures, &xgcvalues);
570 /* Send orders to server, and wait until window is displayed - three
571 * events must be received: a MapNotify event, an Expose event allowing
572 * drawing in the window, and a ConfigureNotify to get the window
573 * dimensions. Once those events have been received, only ConfigureNotify
574 * events need to be received. */
576 b_configure_notify = 0;
578 XMapWindow( p_vout->p_sys->p_display, p_vout->p_sys->window);
581 XNextEvent( p_vout->p_sys->p_display, &xevent);
582 if( (xevent.type == Expose)
583 && (xevent.xexpose.window == p_vout->p_sys->window) )
587 else if( (xevent.type == MapNotify)
588 && (xevent.xmap.window == p_vout->p_sys->window) )
592 else if( (xevent.type == ConfigureNotify)
593 && (xevent.xconfigure.window == p_vout->p_sys->window) )
595 b_configure_notify = 1;
596 p_vout->p_sys->i_width = xevent.xconfigure.width;
597 p_vout->p_sys->i_height = xevent.xconfigure.height;
599 } while( !( b_expose && b_configure_notify && b_map_notify ) );
601 XSelectInput( p_vout->p_sys->p_display, p_vout->p_sys->window,
602 StructureNotifyMask | KeyPressMask |
603 ButtonPressMask | ButtonReleaseMask |
606 if( p_vout->b_fullscreen )
608 XSetInputFocus( p_vout->p_sys->p_display, p_vout->p_sys->window,
609 RevertToNone, CurrentTime );
610 XMoveWindow( p_vout->p_sys->p_display, p_vout->p_sys->window, 0, 0 );
613 #ifdef MODULE_NAME_IS_x11
614 if( XDefaultDepth(p_vout->p_sys->p_display, p_vout->p_sys->i_screen) == 8 )
616 /* Allocate a new palette */
617 p_vout->p_sys->colormap =
618 XCreateColormap( p_vout->p_sys->p_display,
619 DefaultRootWindow( p_vout->p_sys->p_display ),
620 DefaultVisual( p_vout->p_sys->p_display,
621 p_vout->p_sys->i_screen ),
624 xwindow_attributes.colormap = p_vout->p_sys->colormap;
625 XChangeWindowAttributes( p_vout->p_sys->p_display,
626 p_vout->p_sys->window,
627 CWColormap, &xwindow_attributes );
631 /* Create YUV output sub-window. */
632 p_vout->p_sys->yuv_window=XCreateSimpleWindow( p_vout->p_sys->p_display,
633 p_vout->p_sys->window, 0, 0, 1, 1, 0,
634 BlackPixel( p_vout->p_sys->p_display,
635 p_vout->p_sys->i_screen ),
636 WhitePixel( p_vout->p_sys->p_display,
637 p_vout->p_sys->i_screen ) );
639 p_vout->p_sys->yuv_gc = XCreateGC( p_vout->p_sys->p_display,
640 p_vout->p_sys->yuv_window,
641 GCGraphicsExposures, &xgcvalues );
643 XSetWindowBackground( p_vout->p_sys->p_display, p_vout->p_sys->yuv_window,
644 BlackPixel(p_vout->p_sys->p_display, p_vout->p_sys->i_screen ) );
646 XMapWindow( p_vout->p_sys->p_display, p_vout->p_sys->yuv_window );
647 XSelectInput( p_vout->p_sys->p_display, p_vout->p_sys->yuv_window,
651 /* If the cursor was formerly blank than blank it again */
652 if( !p_vout->p_sys->b_mouse_pointer_visible )
654 _M( XCommonToggleMousePointer ) ( p_vout );
655 _M( XCommonToggleMousePointer ) ( p_vout );
658 XSync( p_vout->p_sys->p_display, False );
660 /* At this stage, the window is open, displayed, and ready to
666 void _M( XCommonDestroyWindow ) ( vout_thread_t *p_vout )
668 XSync( p_vout->p_sys->p_display, False );
670 #ifdef MODULE_NAME_IS_xvideo
671 XFreeGC( p_vout->p_sys->p_display, p_vout->p_sys->yuv_gc );
672 XDestroyWindow( p_vout->p_sys->p_display, p_vout->p_sys->yuv_window );
675 XUnmapWindow( p_vout->p_sys->p_display, p_vout->p_sys->window );
676 XFreeGC( p_vout->p_sys->p_display, p_vout->p_sys->gc );
677 XDestroyWindow( p_vout->p_sys->p_display, p_vout->p_sys->window );
680 /*****************************************************************************
681 * XCommonEnableScreenSaver: enable screen saver
682 *****************************************************************************
683 * This function enable the screen saver on a display after it had been
684 * disabled by XDisableScreenSaver. Both functions use a counter mechanism to
685 * know wether the screen saver can be activated or not: if n successive calls
686 * are made to XDisableScreenSaver, n successive calls to XEnableScreenSaver
687 * will be required before the screen saver could effectively be activated.
688 *****************************************************************************/
689 void _M( XCommonEnableScreenSaver ) ( vout_thread_t *p_vout )
691 intf_DbgMsg( "vout: enabling screen saver" );
692 XSetScreenSaver( p_vout->p_sys->p_display, p_vout->p_sys->i_ss_timeout,
693 p_vout->p_sys->i_ss_interval,
694 p_vout->p_sys->i_ss_blanking,
695 p_vout->p_sys->i_ss_exposure );
698 /*****************************************************************************
699 * XCommonDisableScreenSaver: disable screen saver
700 *****************************************************************************
701 * See XEnableScreenSaver
702 *****************************************************************************/
703 void _M( XCommonDisableScreenSaver ) ( vout_thread_t *p_vout )
705 /* Save screen saver informations */
706 XGetScreenSaver( p_vout->p_sys->p_display, &p_vout->p_sys->i_ss_timeout,
707 &p_vout->p_sys->i_ss_interval,
708 &p_vout->p_sys->i_ss_blanking,
709 &p_vout->p_sys->i_ss_exposure );
711 /* Disable screen saver */
712 intf_DbgMsg( "vout: disabling screen saver" );
713 XSetScreenSaver( p_vout->p_sys->p_display, 0,
714 p_vout->p_sys->i_ss_interval,
715 p_vout->p_sys->i_ss_blanking,
716 p_vout->p_sys->i_ss_exposure );
718 #ifdef MODULE_NAME_IS_xvideo
719 DPMSDisable( p_vout->p_sys->p_display );
723 /*****************************************************************************
724 * XCommonToggleMousePointer: hide or show the mouse pointer
725 *****************************************************************************
726 * This function hides the X pointer if it is visible by putting it at
727 * coordinates (32,32) and setting the pointer sprite to a blank one. To
728 * show it again, we disable the sprite and restore the original coordinates.
729 *****************************************************************************/
730 void _M( XCommonToggleMousePointer ) ( vout_thread_t *p_vout )
732 if( p_vout->p_sys->b_mouse_pointer_visible )
734 XDefineCursor( p_vout->p_sys->p_display,
735 p_vout->p_sys->window,
736 p_vout->p_sys->blank_cursor );
737 p_vout->p_sys->b_mouse_pointer_visible = 0;
741 XUndefineCursor( p_vout->p_sys->p_display, p_vout->p_sys->window );
742 p_vout->p_sys->b_mouse_pointer_visible = 1;