1 /*****************************************************************************
2 * xmga.c : X11 MGA plugin for vlc
3 *****************************************************************************
4 * Copyright (C) 1998-2001 VideoLAN
5 * $Id: xmga.c,v 1.13 2002/05/06 21:05:26 gbazin 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() */
33 #include <videolan/vlc.h>
35 #ifdef HAVE_MACHINE_PARAM_H
37 #include <machine/param.h>
38 #include <sys/types.h> /* typedef ushort */
43 #include <netinet/in.h> /* BSD: struct in_addr */
46 #include <sys/shm.h> /* shmget(), shmctl() */
48 #include <X11/Xutil.h>
49 #include <X11/keysym.h>
50 #include <X11/extensions/XShm.h>
51 #include <X11/extensions/dpms.h>
54 #include "video_output.h"
56 #include "interface.h"
57 #include "netutils.h" /* network_ChannelJoin */
59 #include "stream_control.h" /* needed by input_ext-intf.h... */
60 #include "input_ext-intf.h"
64 /*****************************************************************************
66 *****************************************************************************/
67 static void vout_getfunctions( function_list_t * );
69 static int vout_Create ( vout_thread_t * );
70 static void vout_Destroy ( vout_thread_t * );
71 static void vout_Render ( vout_thread_t *, picture_t * );
72 static void vout_Display ( vout_thread_t *, picture_t * );
73 static int vout_Manage ( vout_thread_t * );
74 static int vout_Init ( vout_thread_t * );
75 static void vout_End ( vout_thread_t * );
77 static int CreateWindow ( vout_thread_t * );
78 static void DestroyWindow ( vout_thread_t * );
80 static int NewPicture ( vout_thread_t *, picture_t * );
81 static void FreePicture ( vout_thread_t *, picture_t * );
83 static void ToggleFullScreen ( vout_thread_t * );
85 static void EnableXScreenSaver ( vout_thread_t * );
86 static void DisableXScreenSaver ( vout_thread_t * );
88 static void CreateCursor ( vout_thread_t * );
89 static void DestroyCursor ( vout_thread_t * );
90 static void ToggleCursor ( vout_thread_t * );
92 /*****************************************************************************
93 * Building configuration tree
94 *****************************************************************************/
96 #define ALT_FS_TEXT N_("alternate fullscreen method")
97 #define ALT_FS_LONGTEXT N_( \
98 "There are two ways to make a fullscreen window, unfortunately each one " \
99 "has its drawbacks.\n" \
100 "1) Let the window manager handle your fullscreen window (default). But " \
101 "things like taskbars will likely show on top of the video.\n" \
102 "2) Completly bypass the window manager, but then nothing will be able " \
103 "to show on top of the video.")
105 #define DISPLAY_TEXT N_("X11 display name")
106 #define DISPLAY_LONGTEXT N_( \
107 "Specify the X11 hardware display you want to use.\nBy default vlc will " \
108 "use the value of the DISPLAY environment variable.")
111 ADD_CATEGORY_HINT( N_("Miscellaneous"), NULL )
112 ADD_STRING ( "xmga_display", NULL, NULL, DISPLAY_TEXT, DISPLAY_LONGTEXT )
113 ADD_BOOL ( "xmga_altfullscreen", NULL, ALT_FS_TEXT, ALT_FS_LONGTEXT )
117 SET_DESCRIPTION( _("X11 MGA module") )
118 ADD_CAPABILITY( VOUT, 60 )
119 ADD_SHORTCUT( "xmga" )
122 MODULE_ACTIVATE_START
123 vout_getfunctions( &p_module->p_functions->vout );
126 MODULE_DEACTIVATE_START
127 MODULE_DEACTIVATE_STOP
129 /*****************************************************************************
130 * vout_sys_t: video output method descriptor
131 *****************************************************************************
132 * This structure is part of the video output thread descriptor.
133 * It describes the X11 and XVideo specific properties of an output thread.
134 *****************************************************************************/
135 typedef struct vout_sys_s
137 /* Internal settings and properties */
138 Display * p_display; /* display pointer */
140 Visual * p_visual; /* visual pointer */
141 int i_screen; /* screen number */
142 Window window; /* root window */
143 GC gc; /* graphic context instance handler */
145 boolean_t b_shm; /* shared memory extension flag */
147 #ifdef MODULE_NAME_IS_xvideo
148 Window yuv_window; /* sub-window for displaying yuv video
152 Colormap colormap; /* colormap used (8bpp only) */
155 int i_bytes_per_pixel;
156 int i_bytes_per_line;
162 /* X11 generic properties */
164 Atom wm_delete_window;
166 int i_width; /* width of main window */
167 int i_height; /* height of main window */
168 boolean_t b_altfullscreen; /* which fullscreen method */
170 /* Backup of window position and size before fullscreen switch */
175 int i_width_backup_2;
176 int i_height_backup_2;
180 /* Screen saver properties */
181 int i_ss_timeout; /* timeout */
182 int i_ss_interval; /* interval between changes */
183 int i_ss_blanking; /* blanking mode */
184 int i_ss_exposure; /* exposure mode */
185 BOOL b_ss_dpms; /* DPMS mode */
187 /* Mouse pointer properties */
188 boolean_t b_mouse_pointer_visible;
189 mtime_t i_time_mouse_last_moved; /* used to auto-hide pointer*/
190 Cursor blank_cursor; /* the hidden cursor */
191 mtime_t i_time_button_last_pressed; /* to track dbl-clicks */
192 Pixmap cursor_pixmap;
196 /*****************************************************************************
197 * picture_sys_t: direct buffer method descriptor
198 *****************************************************************************
199 * This structure is part of the picture descriptor, it describes the
200 * XVideo specific properties of a direct buffer.
201 *****************************************************************************/
202 typedef struct picture_sys_s
206 /*****************************************************************************
207 * mwmhints_t: window manager hints
208 *****************************************************************************
209 * Fullscreen needs to be able to hide the wm decorations so we provide
210 * this structure to make it easier.
211 *****************************************************************************/
212 #define MWM_HINTS_DECORATIONS (1L << 1)
213 #define PROP_MWM_HINTS_ELEMENTS 5
214 typedef struct mwmhints_s
223 /*****************************************************************************
225 *****************************************************************************/
226 #ifdef MODULE_NAME_IS_xvideo
227 # define MAX_DIRECTBUFFERS 5
229 # define MAX_DIRECTBUFFERS 2
232 /*****************************************************************************
233 * Seeking function TODO: put this in a generic location !
234 *****************************************************************************/
235 static __inline__ void vout_Seek( off_t i_seek )
239 vlc_mutex_lock( &p_input_bank->lock );
240 if( p_input_bank->pp_input[0] != NULL )
242 #define S p_input_bank->pp_input[0]->stream
243 i_tell = S.p_selected_area->i_tell + i_seek * (off_t)50 * S.i_mux_rate;
245 i_tell = ( i_tell <= 0 /*S.p_selected_area->i_start*/ )
246 ? 0 /*S.p_selected_area->i_start*/
247 : ( i_tell >= S.p_selected_area->i_size )
248 ? S.p_selected_area->i_size
251 input_Seek( p_input_bank->pp_input[0], i_tell );
254 vlc_mutex_unlock( &p_input_bank->lock );
257 /*****************************************************************************
258 * Functions exported as capabilities. They are declared as static so that
259 * we don't pollute the namespace too much.
260 *****************************************************************************/
261 static void vout_getfunctions( function_list_t * p_function_list )
263 p_function_list->functions.vout.pf_create = vout_Create;
264 p_function_list->functions.vout.pf_init = vout_Init;
265 p_function_list->functions.vout.pf_end = vout_End;
266 p_function_list->functions.vout.pf_destroy = vout_Destroy;
267 p_function_list->functions.vout.pf_manage = vout_Manage;
268 p_function_list->functions.vout.pf_render = vout_Render;
269 p_function_list->functions.vout.pf_display = vout_Display;
272 /*****************************************************************************
273 * vout_Create: allocate X11 video thread output method
274 *****************************************************************************
275 * This function allocate and initialize a X11 vout method. It uses some of the
276 * vout properties to choose the window size, and change them according to the
277 * actual properties of the display.
278 *****************************************************************************/
279 static int vout_Create( vout_thread_t *p_vout )
283 /* Allocate structure */
284 p_vout->p_sys = malloc( sizeof( vout_sys_t ) );
285 if( p_vout->p_sys == NULL )
287 intf_ErrMsg( "vout error: %s", strerror(ENOMEM) );
291 /* Open display, unsing the "display" config variable or the DISPLAY
292 * environment variable */
293 psz_display = config_GetPszVariable( "xmga_display" );
294 p_vout->p_sys->p_display = XOpenDisplay( psz_display );
296 if( p_vout->p_sys->p_display == NULL ) /* error */
298 intf_ErrMsg( "vout error: cannot open display %s",
299 XDisplayName( psz_display ) );
300 free( p_vout->p_sys );
301 if( psz_display ) free( psz_display );
304 if( psz_display ) free( psz_display );
306 p_vout->p_sys->i_screen = DefaultScreen( p_vout->p_sys->p_display );
308 /* Create blank cursor (for mouse cursor autohiding) */
309 p_vout->p_sys->b_mouse_pointer_visible = 1;
310 CreateCursor( p_vout );
312 /* Spawn base window - this window will include the video output window,
313 * but also command buttons, subtitles and other indicators */
314 if( CreateWindow( p_vout ) )
316 intf_ErrMsg( "vout error: cannot create X11 window" );
317 DestroyCursor( p_vout );
318 XCloseDisplay( p_vout->p_sys->p_display );
319 free( p_vout->p_sys );
323 /* Disable screen saver */
324 DisableXScreenSaver( p_vout );
327 p_vout->p_sys->b_altfullscreen = 0;
332 /*****************************************************************************
333 * vout_Destroy: destroy X11 video thread output method
334 *****************************************************************************
335 * Terminate an output method created by vout_CreateOutputMethod
336 *****************************************************************************/
337 static void vout_Destroy( vout_thread_t *p_vout )
339 /* Restore cursor if it was blanked */
340 if( !p_vout->p_sys->b_mouse_pointer_visible )
342 ToggleCursor( p_vout );
345 DestroyCursor( p_vout );
346 EnableXScreenSaver( p_vout );
347 DestroyWindow( p_vout );
349 XCloseDisplay( p_vout->p_sys->p_display );
351 /* Destroy structure */
352 free( p_vout->p_sys );
355 /*****************************************************************************
356 * vout_Init: initialize X11 video thread output method
357 *****************************************************************************
358 * This function create the XImages needed by the output thread. It is called
359 * at the beginning of the thread, but also each time the window is resized.
360 *****************************************************************************/
361 static int vout_Init( vout_thread_t *p_vout )
366 I_OUTPUTPICTURES = 0;
368 #ifdef MODULE_NAME_IS_xvideo
369 /* Initialize the output structure; we already found an XVideo port,
370 * and the corresponding chroma we will be using. Since we can
371 * arbitrary scale, stick to the coordinates and aspect. */
372 p_vout->output.i_width = p_vout->render.i_width;
373 p_vout->output.i_height = p_vout->render.i_height;
374 p_vout->output.i_aspect = p_vout->render.i_aspect;
377 /* Initialize the output structure: RGB with square pixels, whatever
378 * the input format is, since it's the only format we know */
379 switch( p_vout->p_sys->i_screen_depth )
381 case 8: /* FIXME: set the palette */
382 p_vout->output.i_chroma = FOURCC_RGB2; break;
384 p_vout->output.i_chroma = FOURCC_RV15; break;
386 p_vout->output.i_chroma = FOURCC_RV16; break;
388 p_vout->output.i_chroma = FOURCC_RV24; break;
390 p_vout->output.i_chroma = FOURCC_RV32; break;
392 intf_ErrMsg( "vout error: unknown screen depth" );
396 p_vout->output.i_width = p_vout->p_sys->i_width;
397 p_vout->output.i_height = p_vout->p_sys->i_height;
399 /* Assume we have square pixels */
400 p_vout->output.i_aspect = p_vout->p_sys->i_width
401 * VOUT_ASPECT_FACTOR / p_vout->p_sys->i_height;
404 /* Try to initialize up to MAX_DIRECTBUFFERS direct buffers */
405 while( I_OUTPUTPICTURES < MAX_DIRECTBUFFERS )
409 /* Find an empty picture slot */
410 for( i_index = 0 ; i_index < VOUT_MAX_PICTURES ; i_index++ )
412 if( p_vout->p_picture[ i_index ].i_status == FREE_PICTURE )
414 p_pic = p_vout->p_picture + i_index;
419 /* Allocate the picture */
420 if( p_pic == NULL || NewPicture( p_vout, p_pic ) )
425 p_pic->i_status = DESTROYED_PICTURE;
426 p_pic->i_type = DIRECT_PICTURE;
428 PP_OUTPUTPICTURE[ I_OUTPUTPICTURES ] = p_pic;
436 /*****************************************************************************
437 * vout_Render: render previously calculated output
438 *****************************************************************************/
439 static void vout_Render( vout_thread_t *p_vout, picture_t *p_pic )
444 /*****************************************************************************
445 * vout_Display: displays previously rendered output
446 *****************************************************************************
447 * This function sends the currently rendered image to X11 server.
448 * (The Xv extension takes care of "double-buffering".)
449 *****************************************************************************/
450 static void vout_Display( vout_thread_t *p_vout, picture_t *p_pic )
452 int i_width, i_height, i_x, i_y;
454 vout_PlacePicture( p_vout, p_vout->p_sys->i_width, p_vout->p_sys->i_height,
455 &i_x, &i_y, &i_width, &i_height );
458 /*****************************************************************************
459 * vout_Manage: handle X11 events
460 *****************************************************************************
461 * This function should be called regularly by video output thread. It manages
462 * X11 events and allows window resizing. It returns a non null value on
464 *****************************************************************************/
465 static int vout_Manage( vout_thread_t *p_vout )
467 XEvent xevent; /* X11 event */
468 boolean_t b_resized; /* window has been resized */
469 char i_key; /* ISO Latin-1 key */
472 /* Handle X11 events: ConfigureNotify events are parsed to know if the
473 * output window's size changed, MapNotify and UnmapNotify to know if the
474 * window is mapped (and if the display is useful), and ClientMessages
475 * to intercept window destruction requests */
478 while( XCheckWindowEvent( p_vout->p_sys->p_display, p_vout->p_sys->window,
479 StructureNotifyMask | KeyPressMask |
480 ButtonPressMask | ButtonReleaseMask |
481 PointerMotionMask | Button1MotionMask , &xevent )
484 /* ConfigureNotify event: prepare */
485 if( (xevent.type == ConfigureNotify)
486 && ((xevent.xconfigure.width != p_vout->p_sys->i_width)
487 || (xevent.xconfigure.height != p_vout->p_sys->i_height)) )
489 /* Update dimensions */
491 p_vout->i_changes |= VOUT_SIZE_CHANGE;
492 p_vout->p_sys->i_width = xevent.xconfigure.width;
493 p_vout->p_sys->i_height = xevent.xconfigure.height;
495 /* MapNotify event: change window status and disable screen saver */
496 else if( xevent.type == MapNotify)
498 if( (p_vout != NULL) && !p_vout->b_active )
500 DisableXScreenSaver( p_vout );
501 p_vout->b_active = 1;
504 /* UnmapNotify event: change window status and enable screen saver */
505 else if( xevent.type == UnmapNotify )
507 if( (p_vout != NULL) && p_vout->b_active )
509 EnableXScreenSaver( p_vout );
510 p_vout->b_active = 0;
514 else if( xevent.type == KeyPress )
516 /* We may have keys like F1 trough F12, ESC ... */
517 x_key_symbol = XKeycodeToKeysym( p_vout->p_sys->p_display,
518 xevent.xkey.keycode, 0 );
519 switch( x_key_symbol )
522 p_main->p_intf->b_die = 1;
525 p_main->p_intf->b_menu_change = 1;
540 input_Seek( p_input_bank->pp_input[0],
541 p_input_bank->pp_input[0]->stream.p_selected_area->i_start );
544 input_Seek( p_input_bank->pp_input[0],
545 p_input_bank->pp_input[0]->stream.p_selected_area->i_size );
554 input_SetStatus( p_input_bank->pp_input[0],
555 INPUT_STATUS_PAUSE );
560 * The reason why I use this instead of XK_0 is that
561 * with XLookupString, we don't have to care about
564 if( XLookupString( &xevent.xkey, &i_key, 1, NULL, NULL ) )
566 /* FIXME: handle stuff here */
571 p_main->p_intf->b_die = 1;
575 p_vout->i_changes |= VOUT_FULLSCREEN_CHANGE;
578 case '0': network_ChannelJoin( 0 ); break;
579 case '1': network_ChannelJoin( 1 ); break;
580 case '2': network_ChannelJoin( 2 ); break;
581 case '3': network_ChannelJoin( 3 ); break;
582 case '4': network_ChannelJoin( 4 ); break;
583 case '5': network_ChannelJoin( 5 ); break;
584 case '6': network_ChannelJoin( 6 ); break;
585 case '7': network_ChannelJoin( 7 ); break;
586 case '8': network_ChannelJoin( 8 ); break;
587 case '9': network_ChannelJoin( 9 ); break;
597 else if( xevent.type == ButtonPress )
599 switch( ((XButtonEvent *)&xevent)->button )
602 /* In this part we will eventually manage
603 * clicks for DVD navigation for instance. */
605 /* detect double-clicks */
606 if( ( ((XButtonEvent *)&xevent)->time -
607 p_vout->p_sys->i_time_button_last_pressed ) < 300 )
609 p_vout->i_changes |= VOUT_FULLSCREEN_CHANGE;
612 p_vout->p_sys->i_time_button_last_pressed =
613 ((XButtonEvent *)&xevent)->time;
626 else if( xevent.type == ButtonRelease )
628 switch( ((XButtonEvent *)&xevent)->button )
631 /* FIXME: need locking ! */
632 p_main->p_intf->b_menu_change = 1;
637 else if( xevent.type == MotionNotify )
639 p_vout->p_sys->i_time_mouse_last_moved = mdate();
640 if( ! p_vout->p_sys->b_mouse_pointer_visible )
642 ToggleCursor( p_vout );
648 intf_WarnMsg( 3, "vout: unhandled event %d received", xevent.type );
652 /* ClientMessage event - only WM_PROTOCOLS with WM_DELETE_WINDOW data
653 * are handled - according to the man pages, the format is always 32
655 while( XCheckTypedEvent( p_vout->p_sys->p_display,
656 ClientMessage, &xevent ) )
658 if( (xevent.xclient.message_type == p_vout->p_sys->wm_protocols)
659 && (xevent.xclient.data.l[0] == p_vout->p_sys->wm_delete_window ) )
661 p_main->p_intf->b_die = 1;
668 if ( p_vout->i_changes & VOUT_FULLSCREEN_CHANGE )
670 ToggleFullScreen( p_vout );
671 p_vout->i_changes &= ~VOUT_FULLSCREEN_CHANGE;
678 if( p_vout->i_changes & VOUT_SIZE_CHANGE )
680 int i_width, i_height, i_x, i_y;
682 p_vout->i_changes &= ~VOUT_SIZE_CHANGE;
684 intf_WarnMsg( 3, "vout: video display resized (%dx%d)",
685 p_vout->p_sys->i_width,
686 p_vout->p_sys->i_height );
688 vout_PlacePicture( p_vout, p_vout->p_sys->i_width,
689 p_vout->p_sys->i_height,
690 &i_x, &i_y, &i_width, &i_height );
693 /* Autohide Cursour */
694 if( mdate() - p_vout->p_sys->i_time_mouse_last_moved > 2000000 )
696 /* Hide the mouse automatically */
697 if( p_vout->p_sys->b_mouse_pointer_visible )
699 ToggleCursor( p_vout );
706 /*****************************************************************************
707 * vout_End: terminate X11 video thread output method
708 *****************************************************************************
709 * Destroy the X11 XImages created by vout_Init. It is called at the end of
710 * the thread, but also each time the window is resized.
711 *****************************************************************************/
712 static void vout_End( vout_thread_t *p_vout )
716 /* Free the direct buffers we allocated */
717 for( i_index = I_OUTPUTPICTURES ; i_index ; )
720 FreePicture( p_vout, PP_OUTPUTPICTURE[ i_index ] );
724 /* following functions are local */
726 /*****************************************************************************
727 * CreateWindow: open and set-up X11 main window
728 *****************************************************************************/
729 static int CreateWindow( vout_thread_t *p_vout )
731 XSizeHints xsize_hints;
732 XSetWindowAttributes xwindow_attributes;
737 boolean_t b_configure_notify;
738 boolean_t b_map_notify;
740 /* Set main window's size */
741 p_vout->p_sys->i_width = p_vout->i_window_width;
742 p_vout->p_sys->i_height = p_vout->i_window_height;
744 /* Prepare window manager hints and properties */
745 xsize_hints.base_width = p_vout->p_sys->i_width;
746 xsize_hints.base_height = p_vout->p_sys->i_height;
747 xsize_hints.flags = PSize;
748 p_vout->p_sys->wm_protocols = XInternAtom( p_vout->p_sys->p_display,
749 "WM_PROTOCOLS", True );
750 p_vout->p_sys->wm_delete_window = XInternAtom( p_vout->p_sys->p_display,
751 "WM_DELETE_WINDOW", True );
753 /* Prepare window attributes */
754 xwindow_attributes.backing_store = Always; /* save the hidden part */
755 xwindow_attributes.background_pixel = BlackPixel(p_vout->p_sys->p_display,
756 p_vout->p_sys->i_screen);
757 xwindow_attributes.event_mask = ExposureMask | StructureNotifyMask;
760 /* Create the window and set hints - the window must receive
761 * ConfigureNotify events, and until it is displayed, Expose and
762 * MapNotify events. */
764 p_vout->p_sys->window =
765 XCreateWindow( p_vout->p_sys->p_display,
766 DefaultRootWindow( p_vout->p_sys->p_display ),
768 p_vout->p_sys->i_width,
769 p_vout->p_sys->i_height,
772 CWBackingStore | CWBackPixel | CWEventMask,
773 &xwindow_attributes );
775 /* Set window manager hints and properties: size hints, command,
776 * window's name, and accepted protocols */
777 XSetWMNormalHints( p_vout->p_sys->p_display, p_vout->p_sys->window,
779 XSetCommand( p_vout->p_sys->p_display, p_vout->p_sys->window,
780 p_main->ppsz_argv, p_main->i_argc );
781 XStoreName( p_vout->p_sys->p_display, p_vout->p_sys->window,
782 VOUT_TITLE " (XMGA output)"
785 if( (p_vout->p_sys->wm_protocols == None) /* use WM_DELETE_WINDOW */
786 || (p_vout->p_sys->wm_delete_window == None)
787 || !XSetWMProtocols( p_vout->p_sys->p_display, p_vout->p_sys->window,
788 &p_vout->p_sys->wm_delete_window, 1 ) )
790 /* WM_DELETE_WINDOW is not supported by window manager */
791 intf_Msg( "vout error: missing or bad window manager" );
794 /* Creation of a graphic context that doesn't generate a GraphicsExpose
795 * event when using functions like XCopyArea */
796 xgcvalues.graphics_exposures = False;
797 p_vout->p_sys->gc = XCreateGC( p_vout->p_sys->p_display,
798 p_vout->p_sys->window,
799 GCGraphicsExposures, &xgcvalues);
801 /* Send orders to server, and wait until window is displayed - three
802 * events must be received: a MapNotify event, an Expose event allowing
803 * drawing in the window, and a ConfigureNotify to get the window
804 * dimensions. Once those events have been received, only ConfigureNotify
805 * events need to be received. */
807 b_configure_notify = 0;
809 XMapWindow( p_vout->p_sys->p_display, p_vout->p_sys->window);
812 XNextEvent( p_vout->p_sys->p_display, &xevent);
813 if( (xevent.type == Expose)
814 && (xevent.xexpose.window == p_vout->p_sys->window) )
818 else if( (xevent.type == MapNotify)
819 && (xevent.xmap.window == p_vout->p_sys->window) )
823 else if( (xevent.type == ConfigureNotify)
824 && (xevent.xconfigure.window == p_vout->p_sys->window) )
826 b_configure_notify = 1;
827 p_vout->p_sys->i_width = xevent.xconfigure.width;
828 p_vout->p_sys->i_height = xevent.xconfigure.height;
830 } while( !( b_expose && b_configure_notify && b_map_notify ) );
832 XSelectInput( p_vout->p_sys->p_display, p_vout->p_sys->window,
833 StructureNotifyMask | KeyPressMask |
834 ButtonPressMask | ButtonReleaseMask |
837 /* If the cursor was formerly blank than blank it again */
838 if( !p_vout->p_sys->b_mouse_pointer_visible )
840 ToggleCursor( p_vout );
841 ToggleCursor( p_vout );
844 XSync( p_vout->p_sys->p_display, False );
846 /* At this stage, the window is open, displayed, and ready to
852 /*****************************************************************************
853 * DestroyWindow: destroy the window
854 *****************************************************************************
856 *****************************************************************************/
857 static void DestroyWindow( vout_thread_t *p_vout )
859 XSync( p_vout->p_sys->p_display, False );
861 XUnmapWindow( p_vout->p_sys->p_display, p_vout->p_sys->window );
862 XFreeGC( p_vout->p_sys->p_display, p_vout->p_sys->gc );
863 XDestroyWindow( p_vout->p_sys->p_display, p_vout->p_sys->window );
866 /*****************************************************************************
867 * NewPicture: allocate a picture
868 *****************************************************************************
869 * Returns 0 on success, -1 otherwise
870 *****************************************************************************/
871 static int NewPicture( vout_thread_t *p_vout, picture_t *p_pic )
873 /* We know the chroma, allocate a buffer which will be used
874 * directly by the decoder */
875 p_pic->p_sys = malloc( sizeof( picture_sys_t ) );
877 if( p_pic->p_sys == NULL )
884 switch( p_vout->output.i_chroma )
889 /* Unknown chroma, tell the guy to get lost */
890 free( p_pic->p_sys );
891 intf_ErrMsg( "vout error: never heard of chroma 0x%.8x (%4.4s)",
892 p_vout->output.i_chroma,
893 (char*)&p_vout->output.i_chroma );
901 /*****************************************************************************
902 * FreePicture: destroy a picture allocated with NewPicture
903 *****************************************************************************
904 * Destroy XImage AND associated data. If using Shm, detach shared memory
905 * segment from server and process, then free it. The XDestroyImage manpage
906 * says that both the image structure _and_ the data pointed to by the
907 * image structure are freed, so no need to free p_image->data.
908 *****************************************************************************/
909 static void FreePicture( vout_thread_t *p_vout, picture_t *p_pic )
912 XSync( p_vout->p_sys->p_display, False );
914 free( p_pic->p_sys );
917 /*****************************************************************************
918 * ToggleFullScreen: Enable or disable full screen mode
919 *****************************************************************************
920 * This function will switch between fullscreen and window mode.
922 *****************************************************************************/
923 static void ToggleFullScreen ( vout_thread_t *p_vout )
927 int i_xpos, i_ypos, i_width, i_height;
929 XSetWindowAttributes attributes;
931 p_vout->b_fullscreen = !p_vout->b_fullscreen;
933 if( p_vout->b_fullscreen )
935 Window next_parent, parent, *p_dummy, dummy1;
936 unsigned int dummy2, dummy3;
938 intf_WarnMsg( 3, "vout: entering fullscreen mode" );
940 /* Only check the fullscreen method when we actually go fullscreen,
941 * because to go back to window mode we need to know in which
942 * fullscreen mode we where */
943 p_vout->p_sys->b_altfullscreen =
944 config_GetIntVariable( "xmga_altfullscreen" );
946 /* Save current window coordinates so they can be restored when
947 * we exit from fullscreen mode. This is the tricky part because
948 * this heavily depends on the behaviour of the window manager.
949 * When you use XMoveWindow some window managers will adjust the top
950 * of the window to the coordinates you gave, but others will instead
951 * adjust the top of the client area to the coordinates
952 * (don't forget windows have decorations). */
954 /* First, get the position and size of the client area */
955 XGetGeometry( p_vout->p_sys->p_display,
956 p_vout->p_sys->window,
960 &p_vout->p_sys->i_width_backup_2,
961 &p_vout->p_sys->i_height_backup_2,
963 XTranslateCoordinates( p_vout->p_sys->p_display,
964 p_vout->p_sys->window,
965 DefaultRootWindow( p_vout->p_sys->p_display ),
968 &p_vout->p_sys->i_xpos_backup_2,
969 &p_vout->p_sys->i_ypos_backup_2,
972 /* Then try to get the position and size of the whole window */
974 /* find the real parent of our window (created by the window manager),
975 * the one which is a direct child of the root window */
976 next_parent = parent = p_vout->p_sys->window;
977 while( next_parent != DefaultRootWindow( p_vout->p_sys->p_display ) )
979 parent = next_parent;
980 XQueryTree( p_vout->p_sys->p_display,
986 XFree((void *)p_dummy);
989 XGetGeometry( p_vout->p_sys->p_display,
990 p_vout->p_sys->window,
994 &p_vout->p_sys->i_width_backup,
995 &p_vout->p_sys->i_height_backup,
998 XTranslateCoordinates( p_vout->p_sys->p_display,
1000 DefaultRootWindow( p_vout->p_sys->p_display ),
1003 &p_vout->p_sys->i_xpos_backup,
1004 &p_vout->p_sys->i_ypos_backup,
1007 /* fullscreen window size and position */
1010 i_width = DisplayWidth( p_vout->p_sys->p_display,
1011 p_vout->p_sys->i_screen );
1012 i_height = DisplayHeight( p_vout->p_sys->p_display,
1013 p_vout->p_sys->i_screen );
1018 intf_WarnMsg( 3, "vout: leaving fullscreen mode" );
1020 i_xpos = p_vout->p_sys->i_xpos_backup;
1021 i_ypos = p_vout->p_sys->i_ypos_backup;
1022 i_width = p_vout->p_sys->i_width_backup;
1023 i_height = p_vout->p_sys->i_height_backup;
1026 /* To my knowledge there are two ways to create a borderless window.
1027 * There's the generic way which is to tell x to bypass the window manager,
1028 * but this creates problems with the focus of other applications.
1029 * The other way is to use the motif property "_MOTIF_WM_HINTS" which
1030 * luckily seems to be supported by most window managers.
1032 if( !p_vout->p_sys->b_altfullscreen )
1034 mwmhints.flags = MWM_HINTS_DECORATIONS;
1035 mwmhints.decorations = !p_vout->b_fullscreen;
1037 prop = XInternAtom( p_vout->p_sys->p_display, "_MOTIF_WM_HINTS",
1039 XChangeProperty( p_vout->p_sys->p_display, p_vout->p_sys->window,
1040 prop, prop, 32, PropModeReplace,
1041 (unsigned char *)&mwmhints,
1042 PROP_MWM_HINTS_ELEMENTS );
1046 /* brute force way to remove decorations */
1047 attributes.override_redirect = p_vout->b_fullscreen;
1048 XChangeWindowAttributes( p_vout->p_sys->p_display,
1049 p_vout->p_sys->window,
1054 /* We need to unmap and remap the window if we want the window
1055 * manager to take our changes into effect */
1056 XUnmapWindow( p_vout->p_sys->p_display, p_vout->p_sys->window);
1058 XWindowEvent( p_vout->p_sys->p_display, p_vout->p_sys->window,
1059 StructureNotifyMask, &xevent );
1060 while( xevent.type != UnmapNotify )
1061 XWindowEvent( p_vout->p_sys->p_display, p_vout->p_sys->window,
1062 StructureNotifyMask, &xevent );
1064 XMapRaised( p_vout->p_sys->p_display, p_vout->p_sys->window);
1066 while( xevent.type != MapNotify )
1067 XWindowEvent( p_vout->p_sys->p_display, p_vout->p_sys->window,
1068 StructureNotifyMask, &xevent );
1070 XMoveResizeWindow( p_vout->p_sys->p_display,
1071 p_vout->p_sys->window,
1077 /* Purge all ConfigureNotify events, this is needed to fix a bug where we
1078 * would lose the original size of the window */
1079 while( xevent.type != ConfigureNotify )
1080 XWindowEvent( p_vout->p_sys->p_display, p_vout->p_sys->window,
1081 StructureNotifyMask, &xevent );
1082 while( XCheckWindowEvent( p_vout->p_sys->p_display, p_vout->p_sys->window,
1083 StructureNotifyMask, &xevent ) );
1086 /* We need to check that the window was really restored where we wanted */
1087 if( !p_vout->b_fullscreen )
1090 unsigned int dummy2, dummy3, dummy4, dummy5;
1092 /* Check the position */
1093 XTranslateCoordinates( p_vout->p_sys->p_display,
1094 p_vout->p_sys->window,
1095 DefaultRootWindow( p_vout->p_sys->p_display ),
1101 if( dummy2 != p_vout->p_sys->i_xpos_backup_2 ||
1102 dummy3 != p_vout->p_sys->i_ypos_backup_2 )
1104 /* Ok it didn't work... second try */
1106 XMoveWindow( p_vout->p_sys->p_display,
1107 p_vout->p_sys->window,
1108 p_vout->p_sys->i_xpos_backup_2,
1109 p_vout->p_sys->i_ypos_backup_2 );
1111 /* Purge all ConfigureNotify events, this is needed to fix a bug
1112 * where we would lose the original size of the window */
1113 XWindowEvent( p_vout->p_sys->p_display, p_vout->p_sys->window,
1114 StructureNotifyMask, &xevent );
1115 while( xevent.type != ConfigureNotify )
1116 XWindowEvent( p_vout->p_sys->p_display, p_vout->p_sys->window,
1117 StructureNotifyMask, &xevent );
1118 while( XCheckWindowEvent( p_vout->p_sys->p_display,
1119 p_vout->p_sys->window,
1120 StructureNotifyMask, &xevent ) );
1123 /* Check the size */
1124 XGetGeometry( p_vout->p_sys->p_display,
1125 p_vout->p_sys->window,
1133 if( dummy4 != p_vout->p_sys->i_width_backup_2 ||
1134 dummy5 != p_vout->p_sys->i_height_backup_2 )
1136 /* Ok it didn't work... third try */
1138 XResizeWindow( p_vout->p_sys->p_display,
1139 p_vout->p_sys->window,
1140 p_vout->p_sys->i_width_backup_2,
1141 p_vout->p_sys->i_height_backup_2 );
1143 /* Purge all ConfigureNotify events, this is needed to fix a bug
1144 * where we would lose the original size of the window */
1145 XWindowEvent( p_vout->p_sys->p_display, p_vout->p_sys->window,
1146 StructureNotifyMask, &xevent );
1147 while( xevent.type != ConfigureNotify )
1148 XWindowEvent( p_vout->p_sys->p_display, p_vout->p_sys->window,
1149 StructureNotifyMask, &xevent );
1150 while( XCheckWindowEvent( p_vout->p_sys->p_display,
1151 p_vout->p_sys->window,
1152 StructureNotifyMask, &xevent ) );
1156 if( p_vout->p_sys->b_altfullscreen )
1157 XSetInputFocus(p_vout->p_sys->p_display,
1158 p_vout->p_sys->window,
1162 /* signal that the size needs to be updated */
1163 p_vout->p_sys->i_width = i_width;
1164 p_vout->p_sys->i_height = i_height;
1165 p_vout->i_changes |= VOUT_SIZE_CHANGE;
1169 /*****************************************************************************
1170 * EnableXScreenSaver: enable screen saver
1171 *****************************************************************************
1172 * This function enables the screen saver on a display after it has been
1173 * disabled by XDisableScreenSaver.
1174 * FIXME: what happens if multiple vlc sessions are running at the same
1176 *****************************************************************************/
1177 static void EnableXScreenSaver( vout_thread_t *p_vout )
1181 XSetScreenSaver( p_vout->p_sys->p_display, p_vout->p_sys->i_ss_timeout,
1182 p_vout->p_sys->i_ss_interval,
1183 p_vout->p_sys->i_ss_blanking,
1184 p_vout->p_sys->i_ss_exposure );
1186 /* Restore DPMS settings */
1187 if( DPMSQueryExtension( p_vout->p_sys->p_display, &dummy, &dummy ) )
1189 if( p_vout->p_sys->b_ss_dpms )
1191 DPMSEnable( p_vout->p_sys->p_display );
1196 /*****************************************************************************
1197 * DisableXScreenSaver: disable screen saver
1198 *****************************************************************************
1199 * See XEnableXScreenSaver
1200 *****************************************************************************/
1201 static void DisableXScreenSaver( vout_thread_t *p_vout )
1205 /* Save screen saver informations */
1206 XGetScreenSaver( p_vout->p_sys->p_display, &p_vout->p_sys->i_ss_timeout,
1207 &p_vout->p_sys->i_ss_interval,
1208 &p_vout->p_sys->i_ss_blanking,
1209 &p_vout->p_sys->i_ss_exposure );
1211 /* Disable screen saver */
1212 XSetScreenSaver( p_vout->p_sys->p_display, 0,
1213 p_vout->p_sys->i_ss_interval,
1214 p_vout->p_sys->i_ss_blanking,
1215 p_vout->p_sys->i_ss_exposure );
1218 if( DPMSQueryExtension( p_vout->p_sys->p_display, &dummy, &dummy ) )
1221 /* Save DPMS current state */
1222 DPMSInfo( p_vout->p_sys->p_display, &dummy,
1223 &p_vout->p_sys->b_ss_dpms );
1224 DPMSDisable( p_vout->p_sys->p_display );
1228 /*****************************************************************************
1229 * CreateCursor: create a blank mouse pointer
1230 *****************************************************************************/
1231 static void CreateCursor( vout_thread_t *p_vout )
1233 XColor cursor_color;
1235 p_vout->p_sys->cursor_pixmap =
1236 XCreatePixmap( p_vout->p_sys->p_display,
1237 DefaultRootWindow( p_vout->p_sys->p_display ),
1240 XParseColor( p_vout->p_sys->p_display,
1241 XCreateColormap( p_vout->p_sys->p_display,
1243 p_vout->p_sys->p_display ),
1245 p_vout->p_sys->p_display,
1246 p_vout->p_sys->i_screen ),
1248 "black", &cursor_color );
1250 p_vout->p_sys->blank_cursor =
1251 XCreatePixmapCursor( p_vout->p_sys->p_display,
1252 p_vout->p_sys->cursor_pixmap,
1253 p_vout->p_sys->cursor_pixmap,
1254 &cursor_color, &cursor_color, 1, 1 );
1257 /*****************************************************************************
1258 * DestroyCursor: destroy the blank mouse pointer
1259 *****************************************************************************/
1260 static void DestroyCursor( vout_thread_t *p_vout )
1262 XFreePixmap( p_vout->p_sys->p_display, p_vout->p_sys->cursor_pixmap );
1265 /*****************************************************************************
1266 * ToggleCursor: hide or show the mouse pointer
1267 *****************************************************************************
1268 * This function hides the X pointer if it is visible by setting the pointer
1269 * sprite to a blank one. To show it again, we disable the sprite.
1270 *****************************************************************************/
1271 static void ToggleCursor( vout_thread_t *p_vout )
1273 if( p_vout->p_sys->b_mouse_pointer_visible )
1275 XDefineCursor( p_vout->p_sys->p_display,
1276 p_vout->p_sys->window,
1277 p_vout->p_sys->blank_cursor );
1278 p_vout->p_sys->b_mouse_pointer_visible = 0;
1282 XUndefineCursor( p_vout->p_sys->p_display, p_vout->p_sys->window );
1283 p_vout->p_sys->b_mouse_pointer_visible = 1;