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.1 2001/12/09 17:01:37 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 /*****************************************************************************
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 int _M( vout_Manage ) ( vout_thread_t *p_vout )
95 XEvent xevent; /* X11 event */
96 boolean_t b_resized; /* window has been resized */
97 char i_key; /* ISO Latin-1 key */
100 /* Handle X11 events: ConfigureNotify events are parsed to know if the
101 * output window's size changed, MapNotify and UnmapNotify to know if the
102 * window is mapped (and if the display is useful), and ClientMessages
103 * to intercept window destruction requests */
106 while( XCheckWindowEvent( p_vout->p_sys->p_display, p_vout->p_sys->window,
107 StructureNotifyMask | KeyPressMask |
108 ButtonPressMask | ButtonReleaseMask |
109 PointerMotionMask | Button1MotionMask , &xevent )
112 /* ConfigureNotify event: prepare */
113 if( (xevent.type == ConfigureNotify)
114 && ((xevent.xconfigure.width != p_vout->p_sys->i_window_width)
115 || (xevent.xconfigure.height != p_vout->p_sys->i_window_height)) )
117 /* Update dimensions */
119 p_vout->p_sys->i_window_width = xevent.xconfigure.width;
120 p_vout->p_sys->i_window_height = xevent.xconfigure.height;
122 /* MapNotify event: change window status and disable screen saver */
123 else if( xevent.type == MapNotify)
125 if( (p_vout != NULL) && !p_vout->b_active )
127 _M( XCommonDisableScreenSaver ) ( p_vout );
128 p_vout->b_active = 1;
131 /* UnmapNotify event: change window status and enable screen saver */
132 else if( xevent.type == UnmapNotify )
134 if( (p_vout != NULL) && p_vout->b_active )
136 _M( XCommonEnableScreenSaver ) ( p_vout );
137 p_vout->b_active = 0;
141 else if( xevent.type == KeyPress )
143 /* We may have keys like F1 trough F12, ESC ... */
144 x_key_symbol = XKeycodeToKeysym( p_vout->p_sys->p_display,
145 xevent.xkey.keycode, 0 );
146 switch( x_key_symbol )
149 p_main->p_intf->b_die = 1;
152 p_main->p_intf->b_menu_change = 1;
156 * The reason why I use this instead of XK_0 is that
157 * with XLookupString, we don't have to care about
160 if( XLookupString( &xevent.xkey, &i_key, 1, NULL, NULL ) )
162 /* FIXME: handle stuff here */
167 p_main->p_intf->b_die = 1;
171 p_vout->i_changes |= VOUT_FULLSCREEN_CHANGE;
174 case '0': network_ChannelJoin( 0 ); break;
175 case '1': network_ChannelJoin( 1 ); break;
176 case '2': network_ChannelJoin( 2 ); break;
177 case '3': network_ChannelJoin( 3 ); break;
178 case '4': network_ChannelJoin( 4 ); break;
179 case '5': network_ChannelJoin( 5 ); break;
180 case '6': network_ChannelJoin( 6 ); break;
181 case '7': network_ChannelJoin( 7 ); break;
182 case '8': network_ChannelJoin( 8 ); break;
183 case '9': network_ChannelJoin( 9 ); break;
186 intf_DbgMsg( "vout: unhandled key '%c' (%i)",
187 (char)i_key, i_key );
195 else if( xevent.type == ButtonPress )
197 switch( ((XButtonEvent *)&xevent)->button )
200 /* In this part we will eventually manage
201 * clicks for DVD navigation for instance. For the
202 * moment just pause the stream. */
203 input_SetStatus( p_main->p_intf->p_input,
204 INPUT_STATUS_PAUSE );
209 else if( xevent.type == ButtonRelease )
211 switch( ((XButtonEvent *)&xevent)->button )
214 /* FIXME: need locking ! */
215 p_main->p_intf->b_menu_change = 1;
220 else if( xevent.type == MotionNotify )
222 p_vout->p_sys->i_time_mouse_last_moved = mdate();
223 if( ! p_vout->p_sys->b_mouse_pointer_visible )
225 _M( XCommonToggleMousePointer ) ( p_vout );
231 intf_WarnMsg( 3, "vout: unhandled event %d received", xevent.type );
235 /* Handle events for YUV video output sub-window */
236 while( XCheckWindowEvent( p_vout->p_sys->p_display,
237 p_vout->p_sys->yuv_window,
238 ExposureMask, &xevent ) == True )
240 /* Window exposed (only handled if stream playback is paused) */
241 if( xevent.type == Expose )
243 if( ((XExposeEvent *)&xevent)->count == 0 )
245 /* (if this is the last a collection of expose events...) */
246 if( p_main->p_intf->p_input != NULL )
249 p_main->p_intf->p_input->stream.control.i_status )
251 /* XVideoDisplay( p_vout )*/;
258 /* ClientMessage event - only WM_PROTOCOLS with WM_DELETE_WINDOW data
259 * are handled - according to the man pages, the format is always 32
261 while( XCheckTypedEvent( p_vout->p_sys->p_display,
262 ClientMessage, &xevent ) )
264 if( (xevent.xclient.message_type == p_vout->p_sys->wm_protocols)
265 && (xevent.xclient.data.l[0] == p_vout->p_sys->wm_delete_window ) )
267 p_main->p_intf->b_die = 1;
271 intf_DbgMsg( "vout: unhandled ClientMessage received" );
275 if ( p_vout->i_changes & VOUT_FULLSCREEN_CHANGE )
277 p_vout->i_changes &= ~VOUT_FULLSCREEN_CHANGE;
279 p_vout->b_fullscreen = !p_vout->b_fullscreen;
281 /* Get rid of the old window */
282 _M( XCommonDestroyWindow ) ( p_vout );
284 /* And create a new one */
285 if( _M( XCommonCreateWindow ) ( p_vout ) )
287 intf_ErrMsg( "vout error: cannot create X11 window" );
288 XCloseDisplay( p_vout->p_sys->p_display );
290 free( p_vout->p_sys );
296 #ifdef MODULE_NAME_IS_x11
298 * Handle vout window resizing
302 /* If interface window has been resized, change vout size */
303 intf_DbgMsg( "vout: resizing output window" );
304 p_vout->i_width = p_vout->p_sys->i_width;
305 p_vout->i_height = p_vout->p_sys->i_height;
306 p_vout->i_changes |= VOUT_SIZE_CHANGE;
308 else if( (p_vout->i_width != p_vout->p_sys->i_width) ||
309 (p_vout->i_height != p_vout->p_sys->i_height) )
311 /* If video output size has changed, change interface window size */
312 intf_DbgMsg( "vout: resizing output window" );
313 p_vout->p_sys->i_width = p_vout->i_width;
314 p_vout->p_sys->i_height = p_vout->i_height;
315 XResizeWindow( p_vout->p_sys->p_display, p_vout->p_sys->window,
316 p_vout->p_sys->i_width, p_vout->p_sys->i_height );
319 * Color/Grayscale or gamma change: in 8bpp, just change the colormap
321 if( (p_vout->i_changes & VOUT_GRAYSCALE_CHANGE)
322 && (p_vout->i_screen_depth == 8) )
324 /* FIXME: clear flags ?? */
330 if( p_vout->i_changes & VOUT_SIZE_CHANGE )
332 intf_DbgMsg( "vout info: resizing window" );
333 p_vout->i_changes &= ~VOUT_SIZE_CHANGE;
336 XResizeWindow( p_vout->p_sys->p_display, p_vout->p_sys->window,
337 p_vout->i_width, p_vout->i_height );
339 /* Destroy XImages to change their size */
342 /* Recreate XImages. If SysInit failed, the thread can't go on. */
343 if( vout_Init( p_vout ) )
345 intf_ErrMsg( "vout error: cannot resize display" );
349 /* Tell the video output thread that it will need to rebuild YUV
350 * tables. This is needed since conversion buffer size may have
352 p_vout->i_changes |= VOUT_YUV_CHANGE;
353 intf_Msg( "vout: video display resized (%dx%d)",
354 p_vout->i_width, p_vout->i_height);
360 if( p_vout->i_changes & VOUT_SIZE_CHANGE )
362 p_vout->i_changes &= ~VOUT_SIZE_CHANGE;
364 p_vout->i_width = p_vout->p_sys->i_window_width;
365 p_vout->i_height = p_vout->p_sys->i_window_height;
367 intf_WarnMsg( 3, "vout: video display resized (%dx%d)",
368 p_vout->i_width, p_vout->i_height );
372 /* Autohide Cursour */
373 if( mdate() - p_vout->p_sys->i_time_mouse_last_moved > 2000000 )
375 /* Hide the mouse automatically */
376 if( p_vout->p_sys->b_mouse_pointer_visible )
378 _M( XCommonToggleMousePointer ) ( p_vout );
385 /*****************************************************************************
386 * XCommonCreateWindow: open and set-up X11 main window
387 *****************************************************************************/
388 int _M( XCommonCreateWindow ) ( vout_thread_t *p_vout )
390 XSizeHints xsize_hints;
391 XSetWindowAttributes xwindow_attributes;
398 boolean_t b_configure_notify;
399 boolean_t b_map_notify;
401 /* If we're full screen, we're full screen! */
402 if( p_vout->b_fullscreen )
404 p_vout->p_sys->i_window_width =
405 DisplayWidth( p_vout->p_sys->p_display, p_vout->p_sys->i_screen );
406 p_vout->p_sys->i_window_height =
407 DisplayHeight( p_vout->p_sys->p_display, p_vout->p_sys->i_screen );
411 /* Set main window's size */
412 p_vout->p_sys->i_window_width = p_vout->i_width;
413 p_vout->p_sys->i_window_height = p_vout->i_height;
416 /* Prepare window manager hints and properties */
417 xsize_hints.base_width = p_vout->p_sys->i_window_width;
418 xsize_hints.base_height = p_vout->p_sys->i_window_height;
419 xsize_hints.flags = PSize;
420 p_vout->p_sys->wm_protocols = XInternAtom( p_vout->p_sys->p_display,
421 "WM_PROTOCOLS", True );
422 p_vout->p_sys->wm_delete_window = XInternAtom( p_vout->p_sys->p_display,
423 "WM_DELETE_WINDOW", True );
425 /* Prepare window attributes */
426 xwindow_attributes.backing_store = Always; /* save the hidden part */
427 xwindow_attributes.background_pixel = BlackPixel( p_vout->p_sys->p_display,
428 p_vout->p_sys->i_screen );
429 xwindow_attributes.event_mask = ExposureMask | StructureNotifyMask;
432 /* Create the window and set hints - the window must receive ConfigureNotify
433 * events, and, until it is displayed, Expose and MapNotify events. */
435 p_vout->p_sys->window =
436 XCreateWindow( p_vout->p_sys->p_display,
437 DefaultRootWindow( p_vout->p_sys->p_display ),
439 p_vout->p_sys->i_window_width,
440 p_vout->p_sys->i_window_height,
441 #ifdef MODULE_NAME_IS_x11
442 /* XXX - what's this ? */
448 CWBackingStore | CWBackPixel | CWEventMask,
449 &xwindow_attributes );
451 if ( p_vout->b_fullscreen )
453 prop = XInternAtom(p_vout->p_sys->p_display, "_MOTIF_WM_HINTS", False);
454 mwmhints.flags = MWM_HINTS_DECORATIONS;
455 mwmhints.decorations = 0;
456 XChangeProperty( p_vout->p_sys->p_display, p_vout->p_sys->window,
457 prop, prop, 32, PropModeReplace,
458 (unsigned char *)&mwmhints, PROP_MWM_HINTS_ELEMENTS );
460 XSetTransientForHint( p_vout->p_sys->p_display,
461 p_vout->p_sys->window, None );
462 XRaiseWindow( p_vout->p_sys->p_display, p_vout->p_sys->window );
465 /* Set window manager hints and properties: size hints, command,
466 * window's name, and accepted protocols */
467 XSetWMNormalHints( p_vout->p_sys->p_display, p_vout->p_sys->window,
469 XSetCommand( p_vout->p_sys->p_display, p_vout->p_sys->window,
470 p_main->ppsz_argv, p_main->i_argc );
471 XStoreName( p_vout->p_sys->p_display, p_vout->p_sys->window,
472 #ifdef MODULE_NAME_IS_x11
473 VOUT_TITLE " (X11 output)"
475 VOUT_TITLE " (XVideo output)"
479 if( (p_vout->p_sys->wm_protocols == None) /* use WM_DELETE_WINDOW */
480 || (p_vout->p_sys->wm_delete_window == None)
481 || !XSetWMProtocols( p_vout->p_sys->p_display, p_vout->p_sys->window,
482 &p_vout->p_sys->wm_delete_window, 1 ) )
484 /* WM_DELETE_WINDOW is not supported by window manager */
485 intf_Msg( "vout error: missing or bad window manager" );
488 /* Creation of a graphic context that doesn't generate a GraphicsExpose
489 * event when using functions like XCopyArea */
490 xgcvalues.graphics_exposures = False;
491 p_vout->p_sys->gc = XCreateGC( p_vout->p_sys->p_display,
492 p_vout->p_sys->window,
493 GCGraphicsExposures, &xgcvalues);
495 /* Send orders to server, and wait until window is displayed - three
496 * events must be received: a MapNotify event, an Expose event allowing
497 * drawing in the window, and a ConfigureNotify to get the window
498 * dimensions. Once those events have been received, only ConfigureNotify
499 * events need to be received. */
501 b_configure_notify = 0;
503 XMapWindow( p_vout->p_sys->p_display, p_vout->p_sys->window);
506 XNextEvent( p_vout->p_sys->p_display, &xevent);
507 if( (xevent.type == Expose)
508 && (xevent.xexpose.window == p_vout->p_sys->window) )
512 else if( (xevent.type == MapNotify)
513 && (xevent.xmap.window == p_vout->p_sys->window) )
517 else if( (xevent.type == ConfigureNotify)
518 && (xevent.xconfigure.window == p_vout->p_sys->window) )
520 b_configure_notify = 1;
521 p_vout->p_sys->i_window_width = xevent.xconfigure.width;
522 p_vout->p_sys->i_window_height = xevent.xconfigure.height;
524 } while( !( b_expose && b_configure_notify && b_map_notify ) );
526 XSelectInput( p_vout->p_sys->p_display, p_vout->p_sys->window,
527 StructureNotifyMask | KeyPressMask |
528 ButtonPressMask | ButtonReleaseMask |
531 if( p_vout->b_fullscreen )
533 XSetInputFocus( p_vout->p_sys->p_display, p_vout->p_sys->window,
534 RevertToNone, CurrentTime );
535 XMoveWindow( p_vout->p_sys->p_display, p_vout->p_sys->window, 0, 0 );
538 #ifdef MODULE_NAME_IS_x11
539 if( XDefaultDepth(p_vout->p_sys->p_display, p_vout->p_sys->i_screen) == 8 )
541 /* Allocate a new palette */
542 p_vout->p_sys->colormap =
543 XCreateColormap( p_vout->p_sys->p_display,
544 DefaultRootWindow( p_vout->p_sys->p_display ),
545 DefaultVisual( p_vout->p_sys->p_display,
546 p_vout->p_sys->i_screen ),
549 xwindow_attributes.colormap = p_vout->p_sys->colormap;
550 XChangeWindowAttributes( p_vout->p_sys->p_display,
551 p_vout->p_sys->window,
552 CWColormap, &xwindow_attributes );
556 /* Create YUV output sub-window. */
557 p_vout->p_sys->yuv_window=XCreateSimpleWindow( p_vout->p_sys->p_display,
558 p_vout->p_sys->window, 0, 0, 1, 1, 0,
559 BlackPixel( p_vout->p_sys->p_display,
560 p_vout->p_sys->i_screen ),
561 WhitePixel( p_vout->p_sys->p_display,
562 p_vout->p_sys->i_screen ) );
564 p_vout->p_sys->yuv_gc = XCreateGC( p_vout->p_sys->p_display,
565 p_vout->p_sys->yuv_window,
566 GCGraphicsExposures, &xgcvalues );
568 XSetWindowBackground( p_vout->p_sys->p_display, p_vout->p_sys->yuv_window,
569 BlackPixel(p_vout->p_sys->p_display, p_vout->p_sys->i_screen ) );
571 XMapWindow( p_vout->p_sys->p_display, p_vout->p_sys->yuv_window );
572 XSelectInput( p_vout->p_sys->p_display, p_vout->p_sys->yuv_window,
576 /* If the cursor was formerly blank than blank it again */
577 if( !p_vout->p_sys->b_mouse_pointer_visible )
579 _M( XCommonToggleMousePointer ) ( p_vout );
580 _M( XCommonToggleMousePointer ) ( p_vout );
583 XSync( p_vout->p_sys->p_display, False );
585 /* At this stage, the window is open, displayed, and ready to
591 void _M( XCommonDestroyWindow ) ( vout_thread_t *p_vout )
593 XSync( p_vout->p_sys->p_display, False );
595 #ifdef MODULE_NAME_IS_xvideo
596 XFreeGC( p_vout->p_sys->p_display, p_vout->p_sys->yuv_gc );
597 XDestroyWindow( p_vout->p_sys->p_display, p_vout->p_sys->yuv_window );
600 XUnmapWindow( p_vout->p_sys->p_display, p_vout->p_sys->window );
601 XFreeGC( p_vout->p_sys->p_display, p_vout->p_sys->gc );
602 XDestroyWindow( p_vout->p_sys->p_display, p_vout->p_sys->window );
605 /*****************************************************************************
606 * XCommonEnableScreenSaver: enable screen saver
607 *****************************************************************************
608 * This function enable the screen saver on a display after it had been
609 * disabled by XDisableScreenSaver. Both functions use a counter mechanism to
610 * know wether the screen saver can be activated or not: if n successive calls
611 * are made to XDisableScreenSaver, n successive calls to XEnableScreenSaver
612 * will be required before the screen saver could effectively be activated.
613 *****************************************************************************/
614 void _M( XCommonEnableScreenSaver ) ( vout_thread_t *p_vout )
616 intf_DbgMsg( "vout: enabling screen saver" );
617 XSetScreenSaver( p_vout->p_sys->p_display, p_vout->p_sys->i_ss_timeout,
618 p_vout->p_sys->i_ss_interval,
619 p_vout->p_sys->i_ss_blanking,
620 p_vout->p_sys->i_ss_exposure );
623 /*****************************************************************************
624 * XCommonDisableScreenSaver: disable screen saver
625 *****************************************************************************
626 * See XEnableScreenSaver
627 *****************************************************************************/
628 void _M( XCommonDisableScreenSaver ) ( vout_thread_t *p_vout )
630 /* Save screen saver informations */
631 XGetScreenSaver( p_vout->p_sys->p_display, &p_vout->p_sys->i_ss_timeout,
632 &p_vout->p_sys->i_ss_interval,
633 &p_vout->p_sys->i_ss_blanking,
634 &p_vout->p_sys->i_ss_exposure );
636 /* Disable screen saver */
637 intf_DbgMsg( "vout: disabling screen saver" );
638 XSetScreenSaver( p_vout->p_sys->p_display, 0,
639 p_vout->p_sys->i_ss_interval,
640 p_vout->p_sys->i_ss_blanking,
641 p_vout->p_sys->i_ss_exposure );
643 #ifdef MODULE_NAME_IS_xvideo
644 DPMSDisable( p_vout->p_sys->p_display );
648 /*****************************************************************************
649 * XCommonToggleMousePointer: hide or show the mouse pointer
650 *****************************************************************************
651 * This function hides the X pointer if it is visible by putting it at
652 * coordinates (32,32) and setting the pointer sprite to a blank one. To
653 * show it again, we disable the sprite and restore the original coordinates.
654 *****************************************************************************/
655 void _M( XCommonToggleMousePointer ) ( vout_thread_t *p_vout )
657 if( p_vout->p_sys->b_mouse_pointer_visible )
659 XDefineCursor( p_vout->p_sys->p_display,
660 p_vout->p_sys->window,
661 p_vout->p_sys->blank_cursor );
662 p_vout->p_sys->b_mouse_pointer_visible = 0;
666 XUndefineCursor( p_vout->p_sys->p_display, p_vout->p_sys->window );
667 p_vout->p_sys->b_mouse_pointer_visible = 1;