1 /*****************************************************************************
2 * xmga.c : X11 MGA plugin for vlc
3 *****************************************************************************
4 * Copyright (C) 1998-2001 VideoLAN
5 * $Id: xmga.c,v 1.20 2002/07/31 20:56:52 sam Exp $
7 * Authors: Vincent Seguin <seguin@via.ecp.fr>
8 * Samuel Hocevar <sam@zoy.org>
9 * Gildas Bazin <gbazin@netcourrier.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 /*****************************************************************************
28 *****************************************************************************/
29 #include <errno.h> /* ENOMEM */
30 #include <stdlib.h> /* free() */
31 #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>
53 #include <X11/extensions/dpms.h>
55 #include "netutils.h" /* network_ChannelJoin */
59 /*****************************************************************************
61 *****************************************************************************/
62 static int Create ( vlc_object_t * );
63 static void Destroy ( vlc_object_t * );
65 static int Init ( vout_thread_t * );
66 static void End ( vout_thread_t * );
67 static int Manage ( vout_thread_t * );
68 static void MGADisplay( vout_thread_t *, picture_t * );
70 static int CreateWindow ( vout_thread_t * );
71 static void DestroyWindow ( vout_thread_t * );
73 static int NewPicture ( vout_thread_t *, picture_t * );
74 static void FreePicture ( vout_thread_t *, picture_t * );
76 static void ToggleFullScreen ( vout_thread_t * );
78 static void EnableXScreenSaver ( vout_thread_t * );
79 static void DisableXScreenSaver ( vout_thread_t * );
81 static void CreateCursor ( vout_thread_t * );
82 static void DestroyCursor ( vout_thread_t * );
83 static void ToggleCursor ( vout_thread_t * );
85 /*****************************************************************************
87 *****************************************************************************/
89 #define ALT_FS_TEXT N_("alternate fullscreen method")
90 #define ALT_FS_LONGTEXT N_( \
91 "There are two ways to make a fullscreen window, unfortunately each one " \
92 "has its drawbacks.\n" \
93 "1) Let the window manager handle your fullscreen window (default). But " \
94 "things like taskbars will likely show on top of the video.\n" \
95 "2) Completly bypass the window manager, but then nothing will be able " \
96 "to show on top of the video.")
98 #define DISPLAY_TEXT N_("X11 display name")
99 #define DISPLAY_LONGTEXT N_( \
100 "Specify the X11 hardware display you want to use. By default vlc will " \
101 "use the value of the DISPLAY environment variable.")
104 add_category_hint( N_("Miscellaneous"), NULL );
105 add_string( "xmga-display", NULL, NULL, DISPLAY_TEXT, DISPLAY_LONGTEXT );
106 add_bool( "xmga-altfullscreen", 0, NULL, ALT_FS_TEXT, ALT_FS_LONGTEXT );
107 set_description( _("X11 MGA module") );
108 set_capability( "video output", 60 );
109 set_callbacks( Create, Destroy );
112 /*****************************************************************************
113 * vout_sys_t: video output method descriptor
114 *****************************************************************************
115 * This structure is part of the video output thread descriptor.
116 * It describes the X11 and XVideo specific properties of an output thread.
117 *****************************************************************************/
120 /* Internal settings and properties */
121 Display * p_display; /* display pointer */
123 Visual * p_visual; /* visual pointer */
124 int i_screen; /* screen number */
125 Window window; /* root window */
126 GC gc; /* graphic context instance handler */
128 vlc_bool_t b_shm; /* shared memory extension flag */
130 #ifdef MODULE_NAME_IS_xvideo
131 Window yuv_window; /* sub-window for displaying yuv video
135 Colormap colormap; /* colormap used (8bpp only) */
138 int i_bytes_per_pixel;
139 int i_bytes_per_line;
145 /* X11 generic properties */
147 Atom wm_delete_window;
149 int i_width; /* width of main window */
150 int i_height; /* height of main window */
151 vlc_bool_t b_altfullscreen; /* which fullscreen method */
153 /* Backup of window position and size before fullscreen switch */
158 int i_width_backup_2;
159 int i_height_backup_2;
163 /* Screen saver properties */
164 int i_ss_timeout; /* timeout */
165 int i_ss_interval; /* interval between changes */
166 int i_ss_blanking; /* blanking mode */
167 int i_ss_exposure; /* exposure mode */
168 BOOL b_ss_dpms; /* DPMS mode */
170 /* Mouse pointer properties */
171 vlc_bool_t b_mouse_pointer_visible;
172 mtime_t i_time_mouse_last_moved; /* used to auto-hide pointer*/
173 Cursor blank_cursor; /* the hidden cursor */
174 mtime_t i_time_button_last_pressed; /* to track dbl-clicks */
175 Pixmap cursor_pixmap;
178 /*****************************************************************************
179 * mwmhints_t: window manager hints
180 *****************************************************************************
181 * Fullscreen needs to be able to hide the wm decorations so we provide
182 * this structure to make it easier.
183 *****************************************************************************/
184 #define MWM_HINTS_DECORATIONS (1L << 1)
185 #define PROP_MWM_HINTS_ELEMENTS 5
186 typedef struct mwmhints_t
195 /*****************************************************************************
197 *****************************************************************************/
198 #ifdef MODULE_NAME_IS_xvideo
199 # define MAX_DIRECTBUFFERS 5
201 # define MAX_DIRECTBUFFERS 2
204 /*****************************************************************************
205 * Create: allocate X11 video thread output method
206 *****************************************************************************
207 * This function allocate and initialize a X11 vout method. It uses some of the
208 * vout properties to choose the window size, and change them according to the
209 * actual properties of the display.
210 *****************************************************************************/
211 static int Create( vlc_object_t *p_this )
213 vout_thread_t *p_vout = (vout_thread_t *)p_this;
216 /* Allocate structure */
217 p_vout->p_sys = malloc( sizeof( vout_sys_t ) );
218 if( p_vout->p_sys == NULL )
220 msg_Err( p_vout, "out of memory" );
224 /* Open display, unsing the "display" config variable or the DISPLAY
225 * environment variable */
226 psz_display = config_GetPsz( p_vout, "xmga-display" );
227 p_vout->p_sys->p_display = XOpenDisplay( psz_display );
229 if( p_vout->p_sys->p_display == NULL ) /* error */
231 msg_Err( p_vout, "cannot open display %s",
232 XDisplayName( psz_display ) );
233 free( p_vout->p_sys );
234 if( psz_display ) free( psz_display );
237 if( psz_display ) free( psz_display );
239 p_vout->p_sys->i_screen = DefaultScreen( p_vout->p_sys->p_display );
241 /* Create blank cursor (for mouse cursor autohiding) */
242 p_vout->p_sys->b_mouse_pointer_visible = 1;
243 CreateCursor( p_vout );
245 /* Spawn base window - this window will include the video output window,
246 * but also command buttons, subtitles and other indicators */
247 if( CreateWindow( p_vout ) )
249 msg_Err( p_vout, "cannot create X11 window" );
250 DestroyCursor( p_vout );
251 XCloseDisplay( p_vout->p_sys->p_display );
252 free( p_vout->p_sys );
256 /* Disable screen saver */
257 DisableXScreenSaver( p_vout );
260 p_vout->p_sys->b_altfullscreen = 0;
262 p_vout->pf_init = Init;
263 p_vout->pf_end = End;
264 p_vout->pf_manage = Manage;
265 p_vout->pf_render = NULL;
266 p_vout->pf_display = MGADisplay;
271 /*****************************************************************************
272 * Destroy: destroy X11 video thread output method
273 *****************************************************************************
274 * Terminate an output method created by Create
275 *****************************************************************************/
276 static void Destroy( vlc_object_t *p_this )
278 vout_thread_t *p_vout = (vout_thread_t *)p_this;
280 /* Restore cursor if it was blanked */
281 if( !p_vout->p_sys->b_mouse_pointer_visible )
283 ToggleCursor( p_vout );
286 DestroyCursor( p_vout );
287 EnableXScreenSaver( p_vout );
288 DestroyWindow( p_vout );
290 XCloseDisplay( p_vout->p_sys->p_display );
292 /* Destroy structure */
293 free( p_vout->p_sys );
296 /*****************************************************************************
297 * Init: initialize X11 video thread output method
298 *****************************************************************************
299 * This function create the XImages needed by the output thread. It is called
300 * at the beginning of the thread, but also each time the window is resized.
301 *****************************************************************************/
302 static int Init( vout_thread_t *p_vout )
307 I_OUTPUTPICTURES = 0;
309 #ifdef MODULE_NAME_IS_xvideo
310 /* Initialize the output structure; we already found an XVideo port,
311 * and the corresponding chroma we will be using. Since we can
312 * arbitrary scale, stick to the coordinates and aspect. */
313 p_vout->output.i_width = p_vout->render.i_width;
314 p_vout->output.i_height = p_vout->render.i_height;
315 p_vout->output.i_aspect = p_vout->render.i_aspect;
318 /* Initialize the output structure: RGB with square pixels, whatever
319 * the input format is, since it's the only format we know */
320 switch( p_vout->p_sys->i_screen_depth )
322 case 8: /* FIXME: set the palette */
323 p_vout->output.i_chroma = VLC_FOURCC('R','G','B','2'); break;
325 p_vout->output.i_chroma = VLC_FOURCC('R','V','1','5'); break;
327 p_vout->output.i_chroma = VLC_FOURCC('R','V','1','6'); break;
329 p_vout->output.i_chroma = VLC_FOURCC('R','V','2','4'); break;
331 p_vout->output.i_chroma = VLC_FOURCC('R','V','3','2'); break;
333 msg_Err( p_vout, "unknown screen depth %i",
334 p_vout->p_sys->i_screen_depth );
338 p_vout->output.i_width = p_vout->p_sys->i_width;
339 p_vout->output.i_height = p_vout->p_sys->i_height;
341 /* Assume we have square pixels */
342 p_vout->output.i_aspect = p_vout->p_sys->i_width
343 * VOUT_ASPECT_FACTOR / p_vout->p_sys->i_height;
346 /* Try to initialize up to MAX_DIRECTBUFFERS direct buffers */
347 while( I_OUTPUTPICTURES < MAX_DIRECTBUFFERS )
351 /* Find an empty picture slot */
352 for( i_index = 0 ; i_index < VOUT_MAX_PICTURES ; i_index++ )
354 if( p_vout->p_picture[ i_index ].i_status == FREE_PICTURE )
356 p_pic = p_vout->p_picture + i_index;
361 /* Allocate the picture */
362 if( p_pic == NULL || NewPicture( p_vout, p_pic ) )
367 p_pic->i_status = DESTROYED_PICTURE;
368 p_pic->i_type = DIRECT_PICTURE;
370 PP_OUTPUTPICTURE[ I_OUTPUTPICTURES ] = p_pic;
378 /*****************************************************************************
379 * MGADisplay: displays previously rendered output
380 *****************************************************************************
381 * This function sends the currently rendered image to X11 server.
382 * (The Xv extension takes care of "double-buffering".)
383 *****************************************************************************/
384 static void MGADisplay( vout_thread_t *p_vout, picture_t *p_pic )
386 int i_width, i_height, i_x, i_y;
388 vout_PlacePicture( p_vout, p_vout->p_sys->i_width, p_vout->p_sys->i_height,
389 &i_x, &i_y, &i_width, &i_height );
392 /*****************************************************************************
393 * Manage: handle X11 events
394 *****************************************************************************
395 * This function should be called regularly by video output thread. It manages
396 * X11 events and allows window resizing. It returns a non null value on
398 *****************************************************************************/
399 static int Manage( vout_thread_t *p_vout )
401 XEvent xevent; /* X11 event */
402 vlc_bool_t b_resized; /* window has been resized */
403 char i_key; /* ISO Latin-1 key */
406 /* Handle X11 events: ConfigureNotify events are parsed to know if the
407 * output window's size changed, MapNotify and UnmapNotify to know if the
408 * window is mapped (and if the display is useful), and ClientMessages
409 * to intercept window destruction requests */
412 while( XCheckWindowEvent( p_vout->p_sys->p_display, p_vout->p_sys->window,
413 StructureNotifyMask | KeyPressMask |
414 ButtonPressMask | ButtonReleaseMask |
415 PointerMotionMask | Button1MotionMask , &xevent )
418 /* ConfigureNotify event: prepare */
419 if( (xevent.type == ConfigureNotify)
420 && ((xevent.xconfigure.width != p_vout->p_sys->i_width)
421 || (xevent.xconfigure.height != p_vout->p_sys->i_height)) )
423 /* Update dimensions */
425 p_vout->i_changes |= VOUT_SIZE_CHANGE;
426 p_vout->p_sys->i_width = xevent.xconfigure.width;
427 p_vout->p_sys->i_height = xevent.xconfigure.height;
430 else if( xevent.type == KeyPress )
432 /* We may have keys like F1 trough F12, ESC ... */
433 x_key_symbol = XKeycodeToKeysym( p_vout->p_sys->p_display,
434 xevent.xkey.keycode, 0 );
435 switch( x_key_symbol )
438 p_vout->p_vlc->b_die = 1;
441 p_vout->p_vlc->p_intf->b_menu_change = 1;
444 input_Seek( p_vout, -5, INPUT_SEEK_SECONDS | INPUT_SEEK_CUR );
447 input_Seek( p_vout, 5, INPUT_SEEK_SECONDS | INPUT_SEEK_CUR );
450 input_Seek( p_vout, 60, INPUT_SEEK_SECONDS | INPUT_SEEK_CUR );
453 input_Seek( p_vout, -60, INPUT_SEEK_SECONDS | INPUT_SEEK_CUR );
456 input_Seek( p_vout, 0, INPUT_SEEK_BYTES | INPUT_SEEK_SET );
459 input_Seek( p_vout, 0, INPUT_SEEK_BYTES | INPUT_SEEK_END );
462 input_Seek( p_vout, 900, INPUT_SEEK_SECONDS | INPUT_SEEK_CUR );
465 input_Seek( p_vout, -900, INPUT_SEEK_SECONDS | INPUT_SEEK_CUR );
468 input_SetStatus( p_input_bank->pp_input[0],
469 INPUT_STATUS_PAUSE );
472 case XK_F1: network_ChannelJoin( p_vout, 1 ); break;
473 case XK_F2: network_ChannelJoin( p_vout, 2 ); break;
474 case XK_F3: network_ChannelJoin( p_vout, 3 ); break;
475 case XK_F4: network_ChannelJoin( p_vout, 4 ); break;
476 case XK_F5: network_ChannelJoin( p_vout, 5 ); break;
477 case XK_F6: network_ChannelJoin( p_vout, 6 ); break;
478 case XK_F7: network_ChannelJoin( p_vout, 7 ); break;
479 case XK_F8: network_ChannelJoin( p_vout, 8 ); break;
480 case XK_F9: network_ChannelJoin( p_vout, 9 ); break;
481 case XK_F10: network_ChannelJoin( p_vout, 10 ); break;
482 case XK_F11: network_ChannelJoin( p_vout, 11 ); break;
483 case XK_F12: network_ChannelJoin( p_vout, 12 ); break;
487 * The reason why I use this instead of XK_0 is that
488 * with XLookupString, we don't have to care about
491 if( XLookupString( &xevent.xkey, &i_key, 1, NULL, NULL ) )
493 /* FIXME: handle stuff here */
498 p_vout->p_vlc->b_die = 1;
502 p_vout->i_changes |= VOUT_FULLSCREEN_CHANGE;
513 else if( xevent.type == ButtonPress )
515 switch( ((XButtonEvent *)&xevent)->button )
518 /* In this part we will eventually manage
519 * clicks for DVD navigation for instance. */
521 /* detect double-clicks */
522 if( ( ((XButtonEvent *)&xevent)->time -
523 p_vout->p_sys->i_time_button_last_pressed ) < 300 )
525 p_vout->i_changes |= VOUT_FULLSCREEN_CHANGE;
528 p_vout->p_sys->i_time_button_last_pressed =
529 ((XButtonEvent *)&xevent)->time;
533 input_Seek( p_vout, 15, INPUT_SEEK_SECONDS | INPUT_SEEK_CUR );
537 input_Seek( p_vout, -15, INPUT_SEEK_SECONDS | INPUT_SEEK_CUR );
542 else if( xevent.type == ButtonRelease )
544 switch( ((XButtonEvent *)&xevent)->button )
547 /* FIXME: need locking ! */
548 p_vout->p_vlc->p_intf->b_menu_change = 1;
553 else if( xevent.type == MotionNotify )
555 p_vout->p_sys->i_time_mouse_last_moved = mdate();
556 if( ! p_vout->p_sys->b_mouse_pointer_visible )
558 ToggleCursor( p_vout );
564 msg_Warn( p_vout, "unhandled event %d received", xevent.type );
568 /* ClientMessage event - only WM_PROTOCOLS with WM_DELETE_WINDOW data
569 * are handled - according to the man pages, the format is always 32
571 while( XCheckTypedEvent( p_vout->p_sys->p_display,
572 ClientMessage, &xevent ) )
574 if( (xevent.xclient.message_type == p_vout->p_sys->wm_protocols)
575 && (xevent.xclient.data.l[0] == p_vout->p_sys->wm_delete_window ) )
577 p_vout->p_vlc->b_die = 1;
584 if ( p_vout->i_changes & VOUT_FULLSCREEN_CHANGE )
586 ToggleFullScreen( p_vout );
587 p_vout->i_changes &= ~VOUT_FULLSCREEN_CHANGE;
594 if( p_vout->i_changes & VOUT_SIZE_CHANGE )
596 int i_width, i_height, i_x, i_y;
598 p_vout->i_changes &= ~VOUT_SIZE_CHANGE;
600 msg_Dbg( p_vout, "video display resized (%dx%d)",
601 p_vout->p_sys->i_width, p_vout->p_sys->i_height );
603 vout_PlacePicture( p_vout, p_vout->p_sys->i_width,
604 p_vout->p_sys->i_height,
605 &i_x, &i_y, &i_width, &i_height );
608 /* Autohide Cursour */
609 if( mdate() - p_vout->p_sys->i_time_mouse_last_moved > 2000000 )
611 /* Hide the mouse automatically */
612 if( p_vout->p_sys->b_mouse_pointer_visible )
614 ToggleCursor( p_vout );
621 /*****************************************************************************
622 * End: terminate X11 video thread output method
623 *****************************************************************************
624 * Destroy the X11 XImages created by Init. It is called at the end of
625 * the thread, but also each time the window is resized.
626 *****************************************************************************/
627 static void End( vout_thread_t *p_vout )
631 /* Free the direct buffers we allocated */
632 for( i_index = I_OUTPUTPICTURES ; i_index ; )
635 FreePicture( p_vout, PP_OUTPUTPICTURE[ i_index ] );
639 /* following functions are local */
641 /*****************************************************************************
642 * CreateWindow: open and set-up X11 main window
643 *****************************************************************************/
644 static int CreateWindow( vout_thread_t *p_vout )
646 XSizeHints xsize_hints;
647 XSetWindowAttributes xwindow_attributes;
652 vlc_bool_t b_configure_notify;
653 vlc_bool_t b_map_notify;
655 /* Set main window's size */
656 p_vout->p_sys->i_width = p_vout->i_window_width;
657 p_vout->p_sys->i_height = p_vout->i_window_height;
659 /* Prepare window manager hints and properties */
660 xsize_hints.base_width = p_vout->p_sys->i_width;
661 xsize_hints.base_height = p_vout->p_sys->i_height;
662 xsize_hints.flags = PSize;
663 p_vout->p_sys->wm_protocols = XInternAtom( p_vout->p_sys->p_display,
664 "WM_PROTOCOLS", True );
665 p_vout->p_sys->wm_delete_window = XInternAtom( p_vout->p_sys->p_display,
666 "WM_DELETE_WINDOW", True );
668 /* Prepare window attributes */
669 xwindow_attributes.backing_store = Always; /* save the hidden part */
670 xwindow_attributes.background_pixel = BlackPixel(p_vout->p_sys->p_display,
671 p_vout->p_sys->i_screen);
672 xwindow_attributes.event_mask = ExposureMask | StructureNotifyMask;
675 /* Create the window and set hints - the window must receive
676 * ConfigureNotify events, and until it is displayed, Expose and
677 * MapNotify events. */
679 p_vout->p_sys->window =
680 XCreateWindow( p_vout->p_sys->p_display,
681 DefaultRootWindow( p_vout->p_sys->p_display ),
683 p_vout->p_sys->i_width,
684 p_vout->p_sys->i_height,
687 CWBackingStore | CWBackPixel | CWEventMask,
688 &xwindow_attributes );
690 /* Set window manager hints and properties: size hints, command,
691 * window's name, and accepted protocols */
692 XSetWMNormalHints( p_vout->p_sys->p_display, p_vout->p_sys->window,
694 XSetCommand( p_vout->p_sys->p_display, p_vout->p_sys->window,
695 p_vout->p_vlc->ppsz_argv, p_vout->p_vlc->i_argc );
696 XStoreName( p_vout->p_sys->p_display, p_vout->p_sys->window,
697 VOUT_TITLE " (XMGA output)"
700 if( (p_vout->p_sys->wm_protocols == None) /* use WM_DELETE_WINDOW */
701 || (p_vout->p_sys->wm_delete_window == None)
702 || !XSetWMProtocols( p_vout->p_sys->p_display, p_vout->p_sys->window,
703 &p_vout->p_sys->wm_delete_window, 1 ) )
705 /* WM_DELETE_WINDOW is not supported by window manager */
706 msg_Err( p_vout, "missing or bad window manager" );
709 /* Creation of a graphic context that doesn't generate a GraphicsExpose
710 * event when using functions like XCopyArea */
711 xgcvalues.graphics_exposures = False;
712 p_vout->p_sys->gc = XCreateGC( p_vout->p_sys->p_display,
713 p_vout->p_sys->window,
714 GCGraphicsExposures, &xgcvalues);
716 /* Send orders to server, and wait until window is displayed - three
717 * events must be received: a MapNotify event, an Expose event allowing
718 * drawing in the window, and a ConfigureNotify to get the window
719 * dimensions. Once those events have been received, only ConfigureNotify
720 * events need to be received. */
722 b_configure_notify = 0;
724 XMapWindow( p_vout->p_sys->p_display, p_vout->p_sys->window);
727 XNextEvent( p_vout->p_sys->p_display, &xevent);
728 if( (xevent.type == Expose)
729 && (xevent.xexpose.window == p_vout->p_sys->window) )
733 else if( (xevent.type == MapNotify)
734 && (xevent.xmap.window == p_vout->p_sys->window) )
738 else if( (xevent.type == ConfigureNotify)
739 && (xevent.xconfigure.window == p_vout->p_sys->window) )
741 b_configure_notify = 1;
742 p_vout->p_sys->i_width = xevent.xconfigure.width;
743 p_vout->p_sys->i_height = xevent.xconfigure.height;
745 } while( !( b_expose && b_configure_notify && b_map_notify ) );
747 XSelectInput( p_vout->p_sys->p_display, p_vout->p_sys->window,
748 StructureNotifyMask | KeyPressMask |
749 ButtonPressMask | ButtonReleaseMask |
752 /* If the cursor was formerly blank than blank it again */
753 if( !p_vout->p_sys->b_mouse_pointer_visible )
755 ToggleCursor( p_vout );
756 ToggleCursor( p_vout );
759 XSync( p_vout->p_sys->p_display, False );
761 /* At this stage, the window is open, displayed, and ready to
767 /*****************************************************************************
768 * DestroyWindow: destroy the window
769 *****************************************************************************
771 *****************************************************************************/
772 static void DestroyWindow( vout_thread_t *p_vout )
774 XSync( p_vout->p_sys->p_display, False );
776 XUnmapWindow( p_vout->p_sys->p_display, p_vout->p_sys->window );
777 XFreeGC( p_vout->p_sys->p_display, p_vout->p_sys->gc );
778 XDestroyWindow( p_vout->p_sys->p_display, p_vout->p_sys->window );
781 /*****************************************************************************
782 * NewPicture: allocate a picture
783 *****************************************************************************
784 * Returns 0 on success, -1 otherwise
785 *****************************************************************************/
786 static int NewPicture( vout_thread_t *p_vout, picture_t *p_pic )
788 /* We know the chroma, allocate a buffer which will be used
789 * directly by the decoder */
790 switch( p_vout->output.i_chroma )
795 /* Unknown chroma, tell the guy to get lost */
796 msg_Err( p_vout, "never heard of chroma 0x%.8x (%4.4s)",
797 p_vout->output.i_chroma, (char*)&p_vout->output.i_chroma );
805 /*****************************************************************************
806 * FreePicture: destroy a picture allocated with NewPicture
807 *****************************************************************************
808 * Destroy XImage AND associated data. If using Shm, detach shared memory
809 * segment from server and process, then free it. The XDestroyImage manpage
810 * says that both the image structure _and_ the data pointed to by the
811 * image structure are freed, so no need to free p_image->data.
812 *****************************************************************************/
813 static void FreePicture( vout_thread_t *p_vout, picture_t *p_pic )
815 XSync( p_vout->p_sys->p_display, False );
818 /*****************************************************************************
819 * ToggleFullScreen: Enable or disable full screen mode
820 *****************************************************************************
821 * This function will switch between fullscreen and window mode.
823 *****************************************************************************/
824 static void ToggleFullScreen ( vout_thread_t *p_vout )
828 int i_xpos, i_ypos, i_width, i_height;
830 XSetWindowAttributes attributes;
832 p_vout->b_fullscreen = !p_vout->b_fullscreen;
834 if( p_vout->b_fullscreen )
836 Window next_parent, parent, *p_dummy, dummy1;
837 unsigned int dummy2, dummy3;
839 msg_Dbg( p_vout, "entering fullscreen mode" );
841 /* Only check the fullscreen method when we actually go fullscreen,
842 * because to go back to window mode we need to know in which
843 * fullscreen mode we where */
844 p_vout->p_sys->b_altfullscreen = config_GetInt( p_vout,
845 "xmga-altfullscreen" );
847 /* Save current window coordinates so they can be restored when
848 * we exit from fullscreen mode. This is the tricky part because
849 * this heavily depends on the behaviour of the window manager.
850 * When you use XMoveWindow some window managers will adjust the top
851 * of the window to the coordinates you gave, but others will instead
852 * adjust the top of the client area to the coordinates
853 * (don't forget windows have decorations). */
855 /* First, get the position and size of the client area */
856 XGetGeometry( p_vout->p_sys->p_display,
857 p_vout->p_sys->window,
861 &p_vout->p_sys->i_width_backup_2,
862 &p_vout->p_sys->i_height_backup_2,
864 XTranslateCoordinates( p_vout->p_sys->p_display,
865 p_vout->p_sys->window,
866 DefaultRootWindow( p_vout->p_sys->p_display ),
869 &p_vout->p_sys->i_xpos_backup_2,
870 &p_vout->p_sys->i_ypos_backup_2,
873 /* Then try to get the position and size of the whole window */
875 /* find the real parent of our window (created by the window manager),
876 * the one which is a direct child of the root window */
877 next_parent = parent = p_vout->p_sys->window;
878 while( next_parent != DefaultRootWindow( p_vout->p_sys->p_display ) )
880 parent = next_parent;
881 XQueryTree( p_vout->p_sys->p_display,
887 XFree((void *)p_dummy);
890 XGetGeometry( p_vout->p_sys->p_display,
891 p_vout->p_sys->window,
895 &p_vout->p_sys->i_width_backup,
896 &p_vout->p_sys->i_height_backup,
899 XTranslateCoordinates( p_vout->p_sys->p_display,
901 DefaultRootWindow( p_vout->p_sys->p_display ),
904 &p_vout->p_sys->i_xpos_backup,
905 &p_vout->p_sys->i_ypos_backup,
908 /* fullscreen window size and position */
911 i_width = DisplayWidth( p_vout->p_sys->p_display,
912 p_vout->p_sys->i_screen );
913 i_height = DisplayHeight( p_vout->p_sys->p_display,
914 p_vout->p_sys->i_screen );
919 msg_Dbg( p_vout, "leaving fullscreen mode" );
921 i_xpos = p_vout->p_sys->i_xpos_backup;
922 i_ypos = p_vout->p_sys->i_ypos_backup;
923 i_width = p_vout->p_sys->i_width_backup;
924 i_height = p_vout->p_sys->i_height_backup;
927 /* To my knowledge there are two ways to create a borderless window.
928 * There's the generic way which is to tell x to bypass the window manager,
929 * but this creates problems with the focus of other applications.
930 * The other way is to use the motif property "_MOTIF_WM_HINTS" which
931 * luckily seems to be supported by most window managers.
933 if( !p_vout->p_sys->b_altfullscreen )
935 mwmhints.flags = MWM_HINTS_DECORATIONS;
936 mwmhints.decorations = !p_vout->b_fullscreen;
938 prop = XInternAtom( p_vout->p_sys->p_display, "_MOTIF_WM_HINTS",
940 XChangeProperty( p_vout->p_sys->p_display, p_vout->p_sys->window,
941 prop, prop, 32, PropModeReplace,
942 (unsigned char *)&mwmhints,
943 PROP_MWM_HINTS_ELEMENTS );
947 /* brute force way to remove decorations */
948 attributes.override_redirect = p_vout->b_fullscreen;
949 XChangeWindowAttributes( p_vout->p_sys->p_display,
950 p_vout->p_sys->window,
955 /* We need to unmap and remap the window if we want the window
956 * manager to take our changes into effect */
957 XUnmapWindow( p_vout->p_sys->p_display, p_vout->p_sys->window);
959 XWindowEvent( p_vout->p_sys->p_display, p_vout->p_sys->window,
960 StructureNotifyMask, &xevent );
961 while( xevent.type != UnmapNotify )
962 XWindowEvent( p_vout->p_sys->p_display, p_vout->p_sys->window,
963 StructureNotifyMask, &xevent );
965 XMapRaised( p_vout->p_sys->p_display, p_vout->p_sys->window);
967 while( xevent.type != MapNotify )
968 XWindowEvent( p_vout->p_sys->p_display, p_vout->p_sys->window,
969 StructureNotifyMask, &xevent );
971 XMoveResizeWindow( p_vout->p_sys->p_display,
972 p_vout->p_sys->window,
978 /* Purge all ConfigureNotify events, this is needed to fix a bug where we
979 * would lose the original size of the window */
980 while( xevent.type != ConfigureNotify )
981 XWindowEvent( p_vout->p_sys->p_display, p_vout->p_sys->window,
982 StructureNotifyMask, &xevent );
983 while( XCheckWindowEvent( p_vout->p_sys->p_display, p_vout->p_sys->window,
984 StructureNotifyMask, &xevent ) );
987 /* We need to check that the window was really restored where we wanted */
988 if( !p_vout->b_fullscreen )
991 unsigned int dummy2, dummy3, dummy4, dummy5;
993 /* Check the position */
994 XTranslateCoordinates( p_vout->p_sys->p_display,
995 p_vout->p_sys->window,
996 DefaultRootWindow( p_vout->p_sys->p_display ),
1002 if( dummy2 != p_vout->p_sys->i_xpos_backup_2 ||
1003 dummy3 != p_vout->p_sys->i_ypos_backup_2 )
1005 /* Ok it didn't work... second try */
1007 XMoveWindow( p_vout->p_sys->p_display,
1008 p_vout->p_sys->window,
1009 p_vout->p_sys->i_xpos_backup_2,
1010 p_vout->p_sys->i_ypos_backup_2 );
1012 /* Purge all ConfigureNotify events, this is needed to fix a bug
1013 * where we would lose the original size of the window */
1014 XWindowEvent( p_vout->p_sys->p_display, p_vout->p_sys->window,
1015 StructureNotifyMask, &xevent );
1016 while( xevent.type != ConfigureNotify )
1017 XWindowEvent( p_vout->p_sys->p_display, p_vout->p_sys->window,
1018 StructureNotifyMask, &xevent );
1019 while( XCheckWindowEvent( p_vout->p_sys->p_display,
1020 p_vout->p_sys->window,
1021 StructureNotifyMask, &xevent ) );
1024 /* Check the size */
1025 XGetGeometry( p_vout->p_sys->p_display,
1026 p_vout->p_sys->window,
1034 if( dummy4 != p_vout->p_sys->i_width_backup_2 ||
1035 dummy5 != p_vout->p_sys->i_height_backup_2 )
1037 /* Ok it didn't work... third try */
1039 XResizeWindow( p_vout->p_sys->p_display,
1040 p_vout->p_sys->window,
1041 p_vout->p_sys->i_width_backup_2,
1042 p_vout->p_sys->i_height_backup_2 );
1044 /* Purge all ConfigureNotify events, this is needed to fix a bug
1045 * where we would lose the original size of the window */
1046 XWindowEvent( p_vout->p_sys->p_display, p_vout->p_sys->window,
1047 StructureNotifyMask, &xevent );
1048 while( xevent.type != ConfigureNotify )
1049 XWindowEvent( p_vout->p_sys->p_display, p_vout->p_sys->window,
1050 StructureNotifyMask, &xevent );
1051 while( XCheckWindowEvent( p_vout->p_sys->p_display,
1052 p_vout->p_sys->window,
1053 StructureNotifyMask, &xevent ) );
1057 if( p_vout->p_sys->b_altfullscreen )
1058 XSetInputFocus(p_vout->p_sys->p_display,
1059 p_vout->p_sys->window,
1063 /* signal that the size needs to be updated */
1064 p_vout->p_sys->i_width = i_width;
1065 p_vout->p_sys->i_height = i_height;
1066 p_vout->i_changes |= VOUT_SIZE_CHANGE;
1070 /*****************************************************************************
1071 * EnableXScreenSaver: enable screen saver
1072 *****************************************************************************
1073 * This function enables the screen saver on a display after it has been
1074 * disabled by XDisableScreenSaver.
1075 * FIXME: what happens if multiple vlc sessions are running at the same
1077 *****************************************************************************/
1078 static void EnableXScreenSaver( vout_thread_t *p_vout )
1082 XSetScreenSaver( p_vout->p_sys->p_display, p_vout->p_sys->i_ss_timeout,
1083 p_vout->p_sys->i_ss_interval,
1084 p_vout->p_sys->i_ss_blanking,
1085 p_vout->p_sys->i_ss_exposure );
1087 /* Restore DPMS settings */
1088 if( DPMSQueryExtension( p_vout->p_sys->p_display, &dummy, &dummy ) )
1090 if( p_vout->p_sys->b_ss_dpms )
1092 DPMSEnable( p_vout->p_sys->p_display );
1097 /*****************************************************************************
1098 * DisableXScreenSaver: disable screen saver
1099 *****************************************************************************
1100 * See XEnableXScreenSaver
1101 *****************************************************************************/
1102 static void DisableXScreenSaver( vout_thread_t *p_vout )
1106 /* Save screen saver informations */
1107 XGetScreenSaver( p_vout->p_sys->p_display, &p_vout->p_sys->i_ss_timeout,
1108 &p_vout->p_sys->i_ss_interval,
1109 &p_vout->p_sys->i_ss_blanking,
1110 &p_vout->p_sys->i_ss_exposure );
1112 /* Disable screen saver */
1113 XSetScreenSaver( p_vout->p_sys->p_display, 0,
1114 p_vout->p_sys->i_ss_interval,
1115 p_vout->p_sys->i_ss_blanking,
1116 p_vout->p_sys->i_ss_exposure );
1119 if( DPMSQueryExtension( p_vout->p_sys->p_display, &dummy, &dummy ) )
1122 /* Save DPMS current state */
1123 DPMSInfo( p_vout->p_sys->p_display, &dummy,
1124 &p_vout->p_sys->b_ss_dpms );
1125 DPMSDisable( p_vout->p_sys->p_display );
1129 /*****************************************************************************
1130 * CreateCursor: create a blank mouse pointer
1131 *****************************************************************************/
1132 static void CreateCursor( vout_thread_t *p_vout )
1134 XColor cursor_color;
1136 p_vout->p_sys->cursor_pixmap =
1137 XCreatePixmap( p_vout->p_sys->p_display,
1138 DefaultRootWindow( p_vout->p_sys->p_display ),
1141 XParseColor( p_vout->p_sys->p_display,
1142 XCreateColormap( p_vout->p_sys->p_display,
1144 p_vout->p_sys->p_display ),
1146 p_vout->p_sys->p_display,
1147 p_vout->p_sys->i_screen ),
1149 "black", &cursor_color );
1151 p_vout->p_sys->blank_cursor =
1152 XCreatePixmapCursor( p_vout->p_sys->p_display,
1153 p_vout->p_sys->cursor_pixmap,
1154 p_vout->p_sys->cursor_pixmap,
1155 &cursor_color, &cursor_color, 1, 1 );
1158 /*****************************************************************************
1159 * DestroyCursor: destroy the blank mouse pointer
1160 *****************************************************************************/
1161 static void DestroyCursor( vout_thread_t *p_vout )
1163 XFreePixmap( p_vout->p_sys->p_display, p_vout->p_sys->cursor_pixmap );
1166 /*****************************************************************************
1167 * ToggleCursor: hide or show the mouse pointer
1168 *****************************************************************************
1169 * This function hides the X pointer if it is visible by setting the pointer
1170 * sprite to a blank one. To show it again, we disable the sprite.
1171 *****************************************************************************/
1172 static void ToggleCursor( vout_thread_t *p_vout )
1174 if( p_vout->p_sys->b_mouse_pointer_visible )
1176 XDefineCursor( p_vout->p_sys->p_display,
1177 p_vout->p_sys->window,
1178 p_vout->p_sys->blank_cursor );
1179 p_vout->p_sys->b_mouse_pointer_visible = 0;
1183 XUndefineCursor( p_vout->p_sys->p_display, p_vout->p_sys->window );
1184 p_vout->p_sys->b_mouse_pointer_visible = 1;