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.2 2001/12/10 10:58:54 massiot 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 /*****************************************************************************
87 * vout_Manage: handle X11 events
88 *****************************************************************************
89 * This function should be called regularly by video output thread. It manages
90 * X11 events and allows window resizing. It returns a non null value on
92 *****************************************************************************/
93 static __inline__ void vout_Seek( int i_seek )
95 int i_tell = p_main->p_intf->p_input->stream.p_selected_area->i_tell;
97 i_tell += i_seek * 50 * p_main->p_intf->p_input->stream.i_mux_rate;
99 if( i_tell < p_main->p_intf->p_input->stream.p_selected_area->i_start )
101 i_tell = p_main->p_intf->p_input->stream.p_selected_area->i_start;
103 else if( i_tell > p_main->p_intf->p_input->stream.p_selected_area->i_size )
105 i_tell = p_main->p_intf->p_input->stream.p_selected_area->i_size;
108 input_Seek( p_main->p_intf->p_input, i_tell );
111 int _M( vout_Manage ) ( vout_thread_t *p_vout )
113 XEvent xevent; /* X11 event */
114 boolean_t b_resized; /* window has been resized */
115 char i_key; /* ISO Latin-1 key */
118 /* Handle X11 events: ConfigureNotify events are parsed to know if the
119 * output window's size changed, MapNotify and UnmapNotify to know if the
120 * window is mapped (and if the display is useful), and ClientMessages
121 * to intercept window destruction requests */
124 while( XCheckWindowEvent( p_vout->p_sys->p_display, p_vout->p_sys->window,
125 StructureNotifyMask | KeyPressMask |
126 ButtonPressMask | ButtonReleaseMask |
127 PointerMotionMask | Button1MotionMask , &xevent )
130 /* ConfigureNotify event: prepare */
131 if( (xevent.type == ConfigureNotify)
132 && ((xevent.xconfigure.width != p_vout->p_sys->i_window_width)
133 || (xevent.xconfigure.height != p_vout->p_sys->i_window_height)) )
135 /* Update dimensions */
137 p_vout->p_sys->i_window_width = xevent.xconfigure.width;
138 p_vout->p_sys->i_window_height = xevent.xconfigure.height;
140 /* MapNotify event: change window status and disable screen saver */
141 else if( xevent.type == MapNotify)
143 if( (p_vout != NULL) && !p_vout->b_active )
145 _M( XCommonDisableScreenSaver ) ( p_vout );
146 p_vout->b_active = 1;
149 /* UnmapNotify event: change window status and enable screen saver */
150 else if( xevent.type == UnmapNotify )
152 if( (p_vout != NULL) && p_vout->b_active )
154 _M( XCommonEnableScreenSaver ) ( p_vout );
155 p_vout->b_active = 0;
159 else if( xevent.type == KeyPress )
161 /* We may have keys like F1 trough F12, ESC ... */
162 x_key_symbol = XKeycodeToKeysym( p_vout->p_sys->p_display,
163 xevent.xkey.keycode, 0 );
164 switch( x_key_symbol )
167 p_main->p_intf->b_die = 1;
170 p_main->p_intf->b_menu_change = 1;
185 input_Seek( p_main->p_intf->p_input,
186 p_main->p_intf->p_input->stream.p_selected_area->i_start );
189 input_Seek( p_main->p_intf->p_input,
190 p_main->p_intf->p_input->stream.p_selected_area->i_size );
199 input_SetStatus( p_main->p_intf->p_input,
200 INPUT_STATUS_PAUSE );
205 * The reason why I use this instead of XK_0 is that
206 * with XLookupString, we don't have to care about
209 if( XLookupString( &xevent.xkey, &i_key, 1, NULL, NULL ) )
211 /* FIXME: handle stuff here */
216 p_main->p_intf->b_die = 1;
220 p_vout->i_changes |= VOUT_FULLSCREEN_CHANGE;
223 case '0': network_ChannelJoin( 0 ); break;
224 case '1': network_ChannelJoin( 1 ); break;
225 case '2': network_ChannelJoin( 2 ); break;
226 case '3': network_ChannelJoin( 3 ); break;
227 case '4': network_ChannelJoin( 4 ); break;
228 case '5': network_ChannelJoin( 5 ); break;
229 case '6': network_ChannelJoin( 6 ); break;
230 case '7': network_ChannelJoin( 7 ); break;
231 case '8': network_ChannelJoin( 8 ); break;
232 case '9': network_ChannelJoin( 9 ); break;
235 intf_DbgMsg( "vout: unhandled key '%c' (%i)",
236 (char)i_key, i_key );
244 else if( xevent.type == ButtonPress )
246 switch( ((XButtonEvent *)&xevent)->button )
249 /* In this part we will eventually manage
250 * clicks for DVD navigation for instance. For the
251 * moment just pause the stream. */
252 input_SetStatus( p_main->p_intf->p_input,
253 INPUT_STATUS_PAUSE );
258 else if( xevent.type == ButtonRelease )
260 switch( ((XButtonEvent *)&xevent)->button )
263 /* FIXME: need locking ! */
264 p_main->p_intf->b_menu_change = 1;
269 else if( xevent.type == MotionNotify )
271 p_vout->p_sys->i_time_mouse_last_moved = mdate();
272 if( ! p_vout->p_sys->b_mouse_pointer_visible )
274 _M( XCommonToggleMousePointer ) ( p_vout );
280 intf_WarnMsg( 3, "vout: unhandled event %d received", xevent.type );
284 /* Handle events for YUV video output sub-window */
285 while( XCheckWindowEvent( p_vout->p_sys->p_display,
286 p_vout->p_sys->yuv_window,
287 ExposureMask, &xevent ) == True )
289 /* Window exposed (only handled if stream playback is paused) */
290 if( xevent.type == Expose )
292 if( ((XExposeEvent *)&xevent)->count == 0 )
294 /* (if this is the last a collection of expose events...) */
295 if( p_main->p_intf->p_input != NULL )
298 p_main->p_intf->p_input->stream.control.i_status )
300 /* XVideoDisplay( p_vout )*/;
307 /* ClientMessage event - only WM_PROTOCOLS with WM_DELETE_WINDOW data
308 * are handled - according to the man pages, the format is always 32
310 while( XCheckTypedEvent( p_vout->p_sys->p_display,
311 ClientMessage, &xevent ) )
313 if( (xevent.xclient.message_type == p_vout->p_sys->wm_protocols)
314 && (xevent.xclient.data.l[0] == p_vout->p_sys->wm_delete_window ) )
316 p_main->p_intf->b_die = 1;
320 intf_DbgMsg( "vout: unhandled ClientMessage received" );
324 if ( p_vout->i_changes & VOUT_FULLSCREEN_CHANGE )
326 p_vout->i_changes &= ~VOUT_FULLSCREEN_CHANGE;
328 p_vout->b_fullscreen = !p_vout->b_fullscreen;
330 /* Get rid of the old window */
331 _M( XCommonDestroyWindow ) ( p_vout );
333 /* And create a new one */
334 if( _M( XCommonCreateWindow ) ( p_vout ) )
336 intf_ErrMsg( "vout error: cannot create X11 window" );
337 XCloseDisplay( p_vout->p_sys->p_display );
339 free( p_vout->p_sys );
345 #ifdef MODULE_NAME_IS_x11
347 * Handle vout window resizing
351 /* If interface window has been resized, change vout size */
352 intf_DbgMsg( "vout: resizing output window" );
353 p_vout->i_width = p_vout->p_sys->i_width;
354 p_vout->i_height = p_vout->p_sys->i_height;
355 p_vout->i_changes |= VOUT_SIZE_CHANGE;
357 else if( (p_vout->i_width != p_vout->p_sys->i_width) ||
358 (p_vout->i_height != p_vout->p_sys->i_height) )
360 /* If video output size has changed, change interface window size */
361 intf_DbgMsg( "vout: resizing output window" );
362 p_vout->p_sys->i_width = p_vout->i_width;
363 p_vout->p_sys->i_height = p_vout->i_height;
364 XResizeWindow( p_vout->p_sys->p_display, p_vout->p_sys->window,
365 p_vout->p_sys->i_width, p_vout->p_sys->i_height );
368 * Color/Grayscale or gamma change: in 8bpp, just change the colormap
370 if( (p_vout->i_changes & VOUT_GRAYSCALE_CHANGE)
371 && (p_vout->i_screen_depth == 8) )
373 /* FIXME: clear flags ?? */
379 if( p_vout->i_changes & VOUT_SIZE_CHANGE )
381 intf_DbgMsg( "vout info: resizing window" );
382 p_vout->i_changes &= ~VOUT_SIZE_CHANGE;
385 XResizeWindow( p_vout->p_sys->p_display, p_vout->p_sys->window,
386 p_vout->i_width, p_vout->i_height );
388 /* Destroy XImages to change their size */
391 /* Recreate XImages. If SysInit failed, the thread can't go on. */
392 if( vout_Init( p_vout ) )
394 intf_ErrMsg( "vout error: cannot resize display" );
398 /* Tell the video output thread that it will need to rebuild YUV
399 * tables. This is needed since conversion buffer size may have
401 p_vout->i_changes |= VOUT_YUV_CHANGE;
402 intf_Msg( "vout: video display resized (%dx%d)",
403 p_vout->i_width, p_vout->i_height);
409 if( p_vout->i_changes & VOUT_SIZE_CHANGE )
411 p_vout->i_changes &= ~VOUT_SIZE_CHANGE;
413 p_vout->i_width = p_vout->p_sys->i_window_width;
414 p_vout->i_height = p_vout->p_sys->i_window_height;
416 intf_WarnMsg( 3, "vout: video display resized (%dx%d)",
417 p_vout->i_width, p_vout->i_height );
421 /* Autohide Cursour */
422 if( mdate() - p_vout->p_sys->i_time_mouse_last_moved > 2000000 )
424 /* Hide the mouse automatically */
425 if( p_vout->p_sys->b_mouse_pointer_visible )
427 _M( XCommonToggleMousePointer ) ( p_vout );
434 /*****************************************************************************
435 * XCommonCreateWindow: open and set-up X11 main window
436 *****************************************************************************/
437 int _M( XCommonCreateWindow ) ( vout_thread_t *p_vout )
439 XSizeHints xsize_hints;
440 XSetWindowAttributes xwindow_attributes;
447 boolean_t b_configure_notify;
448 boolean_t b_map_notify;
450 /* If we're full screen, we're full screen! */
451 if( p_vout->b_fullscreen )
453 p_vout->p_sys->i_window_width =
454 DisplayWidth( p_vout->p_sys->p_display, p_vout->p_sys->i_screen );
455 p_vout->p_sys->i_window_height =
456 DisplayHeight( p_vout->p_sys->p_display, p_vout->p_sys->i_screen );
460 /* Set main window's size */
461 p_vout->p_sys->i_window_width = p_vout->i_width;
462 p_vout->p_sys->i_window_height = p_vout->i_height;
465 /* Prepare window manager hints and properties */
466 xsize_hints.base_width = p_vout->p_sys->i_window_width;
467 xsize_hints.base_height = p_vout->p_sys->i_window_height;
468 xsize_hints.flags = PSize;
469 p_vout->p_sys->wm_protocols = XInternAtom( p_vout->p_sys->p_display,
470 "WM_PROTOCOLS", True );
471 p_vout->p_sys->wm_delete_window = XInternAtom( p_vout->p_sys->p_display,
472 "WM_DELETE_WINDOW", True );
474 /* Prepare window attributes */
475 xwindow_attributes.backing_store = Always; /* save the hidden part */
476 xwindow_attributes.background_pixel = BlackPixel( p_vout->p_sys->p_display,
477 p_vout->p_sys->i_screen );
478 xwindow_attributes.event_mask = ExposureMask | StructureNotifyMask;
481 /* Create the window and set hints - the window must receive ConfigureNotify
482 * events, and, until it is displayed, Expose and MapNotify events. */
484 p_vout->p_sys->window =
485 XCreateWindow( p_vout->p_sys->p_display,
486 DefaultRootWindow( p_vout->p_sys->p_display ),
488 p_vout->p_sys->i_window_width,
489 p_vout->p_sys->i_window_height,
490 #ifdef MODULE_NAME_IS_x11
491 /* XXX - what's this ? */
497 CWBackingStore | CWBackPixel | CWEventMask,
498 &xwindow_attributes );
500 if ( p_vout->b_fullscreen )
502 prop = XInternAtom(p_vout->p_sys->p_display, "_MOTIF_WM_HINTS", False);
503 mwmhints.flags = MWM_HINTS_DECORATIONS;
504 mwmhints.decorations = 0;
505 XChangeProperty( p_vout->p_sys->p_display, p_vout->p_sys->window,
506 prop, prop, 32, PropModeReplace,
507 (unsigned char *)&mwmhints, PROP_MWM_HINTS_ELEMENTS );
509 XSetTransientForHint( p_vout->p_sys->p_display,
510 p_vout->p_sys->window, None );
511 XRaiseWindow( p_vout->p_sys->p_display, p_vout->p_sys->window );
514 /* Set window manager hints and properties: size hints, command,
515 * window's name, and accepted protocols */
516 XSetWMNormalHints( p_vout->p_sys->p_display, p_vout->p_sys->window,
518 XSetCommand( p_vout->p_sys->p_display, p_vout->p_sys->window,
519 p_main->ppsz_argv, p_main->i_argc );
520 XStoreName( p_vout->p_sys->p_display, p_vout->p_sys->window,
521 #ifdef MODULE_NAME_IS_x11
522 VOUT_TITLE " (X11 output)"
524 VOUT_TITLE " (XVideo output)"
528 if( (p_vout->p_sys->wm_protocols == None) /* use WM_DELETE_WINDOW */
529 || (p_vout->p_sys->wm_delete_window == None)
530 || !XSetWMProtocols( p_vout->p_sys->p_display, p_vout->p_sys->window,
531 &p_vout->p_sys->wm_delete_window, 1 ) )
533 /* WM_DELETE_WINDOW is not supported by window manager */
534 intf_Msg( "vout error: missing or bad window manager" );
537 /* Creation of a graphic context that doesn't generate a GraphicsExpose
538 * event when using functions like XCopyArea */
539 xgcvalues.graphics_exposures = False;
540 p_vout->p_sys->gc = XCreateGC( p_vout->p_sys->p_display,
541 p_vout->p_sys->window,
542 GCGraphicsExposures, &xgcvalues);
544 /* Send orders to server, and wait until window is displayed - three
545 * events must be received: a MapNotify event, an Expose event allowing
546 * drawing in the window, and a ConfigureNotify to get the window
547 * dimensions. Once those events have been received, only ConfigureNotify
548 * events need to be received. */
550 b_configure_notify = 0;
552 XMapWindow( p_vout->p_sys->p_display, p_vout->p_sys->window);
555 XNextEvent( p_vout->p_sys->p_display, &xevent);
556 if( (xevent.type == Expose)
557 && (xevent.xexpose.window == p_vout->p_sys->window) )
561 else if( (xevent.type == MapNotify)
562 && (xevent.xmap.window == p_vout->p_sys->window) )
566 else if( (xevent.type == ConfigureNotify)
567 && (xevent.xconfigure.window == p_vout->p_sys->window) )
569 b_configure_notify = 1;
570 p_vout->p_sys->i_window_width = xevent.xconfigure.width;
571 p_vout->p_sys->i_window_height = xevent.xconfigure.height;
573 } while( !( b_expose && b_configure_notify && b_map_notify ) );
575 XSelectInput( p_vout->p_sys->p_display, p_vout->p_sys->window,
576 StructureNotifyMask | KeyPressMask |
577 ButtonPressMask | ButtonReleaseMask |
580 if( p_vout->b_fullscreen )
582 XSetInputFocus( p_vout->p_sys->p_display, p_vout->p_sys->window,
583 RevertToNone, CurrentTime );
584 XMoveWindow( p_vout->p_sys->p_display, p_vout->p_sys->window, 0, 0 );
587 #ifdef MODULE_NAME_IS_x11
588 if( XDefaultDepth(p_vout->p_sys->p_display, p_vout->p_sys->i_screen) == 8 )
590 /* Allocate a new palette */
591 p_vout->p_sys->colormap =
592 XCreateColormap( p_vout->p_sys->p_display,
593 DefaultRootWindow( p_vout->p_sys->p_display ),
594 DefaultVisual( p_vout->p_sys->p_display,
595 p_vout->p_sys->i_screen ),
598 xwindow_attributes.colormap = p_vout->p_sys->colormap;
599 XChangeWindowAttributes( p_vout->p_sys->p_display,
600 p_vout->p_sys->window,
601 CWColormap, &xwindow_attributes );
605 /* Create YUV output sub-window. */
606 p_vout->p_sys->yuv_window=XCreateSimpleWindow( p_vout->p_sys->p_display,
607 p_vout->p_sys->window, 0, 0, 1, 1, 0,
608 BlackPixel( p_vout->p_sys->p_display,
609 p_vout->p_sys->i_screen ),
610 WhitePixel( p_vout->p_sys->p_display,
611 p_vout->p_sys->i_screen ) );
613 p_vout->p_sys->yuv_gc = XCreateGC( p_vout->p_sys->p_display,
614 p_vout->p_sys->yuv_window,
615 GCGraphicsExposures, &xgcvalues );
617 XSetWindowBackground( p_vout->p_sys->p_display, p_vout->p_sys->yuv_window,
618 BlackPixel(p_vout->p_sys->p_display, p_vout->p_sys->i_screen ) );
620 XMapWindow( p_vout->p_sys->p_display, p_vout->p_sys->yuv_window );
621 XSelectInput( p_vout->p_sys->p_display, p_vout->p_sys->yuv_window,
625 /* If the cursor was formerly blank than blank it again */
626 if( !p_vout->p_sys->b_mouse_pointer_visible )
628 _M( XCommonToggleMousePointer ) ( p_vout );
629 _M( XCommonToggleMousePointer ) ( p_vout );
632 XSync( p_vout->p_sys->p_display, False );
634 /* At this stage, the window is open, displayed, and ready to
640 void _M( XCommonDestroyWindow ) ( vout_thread_t *p_vout )
642 XSync( p_vout->p_sys->p_display, False );
644 #ifdef MODULE_NAME_IS_xvideo
645 XFreeGC( p_vout->p_sys->p_display, p_vout->p_sys->yuv_gc );
646 XDestroyWindow( p_vout->p_sys->p_display, p_vout->p_sys->yuv_window );
649 XUnmapWindow( p_vout->p_sys->p_display, p_vout->p_sys->window );
650 XFreeGC( p_vout->p_sys->p_display, p_vout->p_sys->gc );
651 XDestroyWindow( p_vout->p_sys->p_display, p_vout->p_sys->window );
654 /*****************************************************************************
655 * XCommonEnableScreenSaver: enable screen saver
656 *****************************************************************************
657 * This function enable the screen saver on a display after it had been
658 * disabled by XDisableScreenSaver. Both functions use a counter mechanism to
659 * know wether the screen saver can be activated or not: if n successive calls
660 * are made to XDisableScreenSaver, n successive calls to XEnableScreenSaver
661 * will be required before the screen saver could effectively be activated.
662 *****************************************************************************/
663 void _M( XCommonEnableScreenSaver ) ( vout_thread_t *p_vout )
665 intf_DbgMsg( "vout: enabling screen saver" );
666 XSetScreenSaver( p_vout->p_sys->p_display, p_vout->p_sys->i_ss_timeout,
667 p_vout->p_sys->i_ss_interval,
668 p_vout->p_sys->i_ss_blanking,
669 p_vout->p_sys->i_ss_exposure );
672 /*****************************************************************************
673 * XCommonDisableScreenSaver: disable screen saver
674 *****************************************************************************
675 * See XEnableScreenSaver
676 *****************************************************************************/
677 void _M( XCommonDisableScreenSaver ) ( vout_thread_t *p_vout )
679 /* Save screen saver informations */
680 XGetScreenSaver( p_vout->p_sys->p_display, &p_vout->p_sys->i_ss_timeout,
681 &p_vout->p_sys->i_ss_interval,
682 &p_vout->p_sys->i_ss_blanking,
683 &p_vout->p_sys->i_ss_exposure );
685 /* Disable screen saver */
686 intf_DbgMsg( "vout: disabling screen saver" );
687 XSetScreenSaver( p_vout->p_sys->p_display, 0,
688 p_vout->p_sys->i_ss_interval,
689 p_vout->p_sys->i_ss_blanking,
690 p_vout->p_sys->i_ss_exposure );
692 #ifdef MODULE_NAME_IS_xvideo
693 DPMSDisable( p_vout->p_sys->p_display );
697 /*****************************************************************************
698 * XCommonToggleMousePointer: hide or show the mouse pointer
699 *****************************************************************************
700 * This function hides the X pointer if it is visible by putting it at
701 * coordinates (32,32) and setting the pointer sprite to a blank one. To
702 * show it again, we disable the sprite and restore the original coordinates.
703 *****************************************************************************/
704 void _M( XCommonToggleMousePointer ) ( vout_thread_t *p_vout )
706 if( p_vout->p_sys->b_mouse_pointer_visible )
708 XDefineCursor( p_vout->p_sys->p_display,
709 p_vout->p_sys->window,
710 p_vout->p_sys->blank_cursor );
711 p_vout->p_sys->b_mouse_pointer_visible = 0;
715 XUndefineCursor( p_vout->p_sys->p_display, p_vout->p_sys->window );
716 p_vout->p_sys->b_mouse_pointer_visible = 1;