1 /*****************************************************************************
2 * xmga.c : X11 MGA plugin for vlc
3 *****************************************************************************
4 * Copyright (C) 1998-2001 VideoLAN
5 * $Id: xmga.c,v 1.1 2002/01/09 02:01:14 sam Exp $
7 * Authors: Vincent Seguin <seguin@via.ecp.fr>
8 * Samuel Hocevar <sam@zoy.org>
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
23 *****************************************************************************/
25 /*****************************************************************************
27 *****************************************************************************/
28 #include <errno.h> /* ENOMEM */
29 #include <stdlib.h> /* free() */
30 #include <string.h> /* strerror() */
32 #include <videolan/vlc.h>
34 #ifdef HAVE_MACHINE_PARAM_H
36 #include <machine/param.h>
37 #include <sys/types.h> /* typedef ushort */
42 #include <netinet/in.h> /* BSD: struct in_addr */
45 #include <sys/shm.h> /* shmget(), shmctl() */
47 #include <X11/Xutil.h>
48 #include <X11/keysym.h>
49 #include <X11/extensions/XShm.h>
50 #include <X11/extensions/dpms.h>
53 #include "video_output.h"
55 #include "interface.h"
56 #include "netutils.h" /* network_ChannelJoin */
58 #include "stream_control.h" /* needed by input_ext-intf.h... */
59 #include "input_ext-intf.h"
63 /*****************************************************************************
65 *****************************************************************************/
66 static void vout_getfunctions( function_list_t * );
68 static int vout_Probe ( probedata_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 *****************************************************************************/
99 SET_DESCRIPTION( "X11 MGA module" )
100 ADD_CAPABILITY( VOUT, 60 )
101 ADD_SHORTCUT( "xmga" )
104 MODULE_ACTIVATE_START
105 vout_getfunctions( &p_module->p_functions->vout );
108 MODULE_DEACTIVATE_START
109 MODULE_DEACTIVATE_STOP
111 /*****************************************************************************
112 * vout_sys_t: video output method descriptor
113 *****************************************************************************
114 * This structure is part of the video output thread descriptor.
115 * It describes the X11 and XVideo specific properties of an output thread.
116 *****************************************************************************/
117 typedef struct vout_sys_s
119 /* Internal settings and properties */
120 Display * p_display; /* display pointer */
122 Visual * p_visual; /* visual pointer */
123 int i_screen; /* screen number */
124 Window window; /* root window */
125 GC gc; /* graphic context instance handler */
127 boolean_t b_shm; /* shared memory extension flag */
129 #ifdef MODULE_NAME_IS_xvideo
130 Window yuv_window; /* sub-window for displaying yuv video
134 Colormap colormap; /* colormap used (8bpp only) */
137 int i_bytes_per_pixel;
138 int i_bytes_per_line;
144 /* X11 generic properties */
146 Atom wm_delete_window;
148 int i_width; /* width of main window */
149 int i_height; /* height of main window */
151 /* Backup of window position and size before fullscreen switch */
157 /* Screen saver properties */
158 int i_ss_timeout; /* timeout */
159 int i_ss_interval; /* interval between changes */
160 int i_ss_blanking; /* blanking mode */
161 int i_ss_exposure; /* exposure mode */
162 BOOL b_ss_dpms; /* DPMS mode */
164 /* Mouse pointer properties */
165 boolean_t b_mouse_pointer_visible;
166 mtime_t i_time_mouse_last_moved; /* used to auto-hide pointer*/
167 Cursor blank_cursor; /* the hidden cursor */
168 Pixmap cursor_pixmap;
172 /*****************************************************************************
173 * picture_sys_t: direct buffer method descriptor
174 *****************************************************************************
175 * This structure is part of the picture descriptor, it describes the
176 * XVideo specific properties of a direct buffer.
177 *****************************************************************************/
178 typedef struct picture_sys_s
182 /*****************************************************************************
183 * mwmhints_t: window manager hints
184 *****************************************************************************
185 * Fullscreen needs to be able to hide the wm decorations so we provide
186 * this structure to make it easier.
187 *****************************************************************************/
188 #define MWM_HINTS_DECORATIONS (1L << 1)
189 #define PROP_MWM_HINTS_ELEMENTS 5
190 typedef struct mwmhints_s
199 /*****************************************************************************
201 *****************************************************************************/
202 #ifdef MODULE_NAME_IS_xvideo
203 # define MAX_DIRECTBUFFERS 5
205 # define MAX_DIRECTBUFFERS 2
208 /*****************************************************************************
209 * Seeking function TODO: put this in a generic location !
210 *****************************************************************************/
211 static __inline__ void vout_Seek( off_t i_seek )
215 vlc_mutex_lock( &p_input_bank->lock );
216 if( p_input_bank->pp_input[0] != NULL )
218 #define S p_input_bank->pp_input[0]->stream
219 i_tell = S.p_selected_area->i_tell + i_seek * (off_t)50 * S.i_mux_rate;
221 i_tell = ( i_tell <= 0 /*S.p_selected_area->i_start*/ )
222 ? 0 /*S.p_selected_area->i_start*/
223 : ( i_tell >= S.p_selected_area->i_size )
224 ? S.p_selected_area->i_size
227 input_Seek( p_input_bank->pp_input[0], i_tell );
230 vlc_mutex_unlock( &p_input_bank->lock );
233 /*****************************************************************************
234 * Functions exported as capabilities. They are declared as static so that
235 * we don't pollute the namespace too much.
236 *****************************************************************************/
237 static void vout_getfunctions( function_list_t * p_function_list )
239 p_function_list->pf_probe = vout_Probe;
240 p_function_list->functions.vout.pf_create = vout_Create;
241 p_function_list->functions.vout.pf_init = vout_Init;
242 p_function_list->functions.vout.pf_end = vout_End;
243 p_function_list->functions.vout.pf_destroy = vout_Destroy;
244 p_function_list->functions.vout.pf_manage = vout_Manage;
245 p_function_list->functions.vout.pf_render = vout_Render;
246 p_function_list->functions.vout.pf_display = vout_Display;
249 /*****************************************************************************
250 * vout_Probe: probe the video driver and return a score
251 *****************************************************************************
252 * This function tries to initialize SDL and returns a score to the
253 * plugin manager so that it can select the best plugin.
254 *****************************************************************************/
255 static int vout_Probe( probedata_t *p_data )
257 Display *p_display; /* display pointer */
259 #ifdef MODULE_NAME_IS_xvideo
260 int i_xvport, i_dummy;
263 /* Open display, unsing 'vlc_display' or DISPLAY environment variable */
264 psz_display = XDisplayName( main_GetPszVariable(VOUT_DISPLAY_VAR, NULL) );
265 p_display = XOpenDisplay( psz_display );
266 if( p_display == NULL ) /* error */
268 intf_WarnMsg( 3, "vout: cannot open display %s", psz_display );
272 #ifdef MODULE_NAME_IS_xvideo
273 /* Check that there is an available XVideo port for this format */
274 i_xvport = XVideoGetPort( p_display, p_data->vout.i_chroma, &i_dummy );
277 /* It failed, but it's not completely lost ! We try to open an
278 * XVideo port for a YUY2 picture */
279 i_xvport = XVideoGetPort( p_display, FOURCC_YUY2, &i_dummy );
282 /* It failed, but it's not completely lost ! We try to open an
283 * XVideo port for a simple 16bpp RGB picture */
284 i_xvport = XVideoGetPort( p_display, FOURCC_RV16, &i_dummy );
287 XCloseDisplay( p_display );
292 XVideoReleasePort( p_display, i_xvport );
295 /* Clean-up everyting */
296 XCloseDisplay( p_display );
298 #ifdef MODULE_NAME_IS_xvideo
305 /*****************************************************************************
306 * vout_Create: allocate X11 video thread output method
307 *****************************************************************************
308 * This function allocate and initialize a X11 vout method. It uses some of the
309 * vout properties to choose the window size, and change them according to the
310 * actual properties of the display.
311 *****************************************************************************/
312 static int vout_Create( vout_thread_t *p_vout )
316 /* Allocate structure */
317 p_vout->p_sys = malloc( sizeof( vout_sys_t ) );
318 if( p_vout->p_sys == NULL )
320 intf_ErrMsg( "vout error: %s", strerror(ENOMEM) );
324 /* Open display, unsing 'vlc_display' or DISPLAY environment variable */
325 psz_display = XDisplayName( main_GetPszVariable( VOUT_DISPLAY_VAR, NULL ) );
326 p_vout->p_sys->p_display = XOpenDisplay( psz_display );
328 if( p_vout->p_sys->p_display == NULL ) /* error */
330 intf_ErrMsg( "vout error: cannot open display %s", psz_display );
331 free( p_vout->p_sys );
334 p_vout->p_sys->i_screen = DefaultScreen( p_vout->p_sys->p_display );
336 /* Create blank cursor (for mouse cursor autohiding) */
337 p_vout->p_sys->b_mouse_pointer_visible = 1;
338 CreateCursor( p_vout );
340 /* Spawn base window - this window will include the video output window,
341 * but also command buttons, subtitles and other indicators */
342 if( CreateWindow( p_vout ) )
344 intf_ErrMsg( "vout error: cannot create X11 window" );
345 DestroyCursor( p_vout );
346 XCloseDisplay( p_vout->p_sys->p_display );
347 free( p_vout->p_sys );
351 /* Disable screen saver and return */
352 DisableXScreenSaver( p_vout );
357 /*****************************************************************************
358 * vout_Destroy: destroy X11 video thread output method
359 *****************************************************************************
360 * Terminate an output method created by vout_CreateOutputMethod
361 *****************************************************************************/
362 static void vout_Destroy( vout_thread_t *p_vout )
364 /* Restore cursor if it was blanked */
365 if( !p_vout->p_sys->b_mouse_pointer_visible )
367 ToggleCursor( p_vout );
370 DestroyCursor( p_vout );
371 EnableXScreenSaver( p_vout );
372 DestroyWindow( p_vout );
374 XCloseDisplay( p_vout->p_sys->p_display );
376 /* Destroy structure */
377 free( p_vout->p_sys );
380 /*****************************************************************************
381 * vout_Init: initialize X11 video thread output method
382 *****************************************************************************
383 * This function create the XImages needed by the output thread. It is called
384 * at the beginning of the thread, but also each time the window is resized.
385 *****************************************************************************/
386 static int vout_Init( vout_thread_t *p_vout )
391 I_OUTPUTPICTURES = 0;
393 #ifdef MODULE_NAME_IS_xvideo
394 /* Initialize the output structure; we already found an XVideo port,
395 * and the corresponding chroma we will be using. Since we can
396 * arbitrary scale, stick to the coordinates and aspect. */
397 p_vout->output.i_width = p_vout->render.i_width;
398 p_vout->output.i_height = p_vout->render.i_height;
399 p_vout->output.i_aspect = p_vout->render.i_aspect;
402 /* Initialize the output structure: RGB with square pixels, whatever
403 * the input format is, since it's the only format we know */
404 switch( p_vout->p_sys->i_screen_depth )
406 case 8: /* FIXME: set the palette */
407 p_vout->output.i_chroma = FOURCC_BI_RGB; break;
409 p_vout->output.i_chroma = FOURCC_RV15; break;
411 p_vout->output.i_chroma = FOURCC_RV16; break;
413 p_vout->output.i_chroma = FOURCC_BI_BITFIELDS; break;
415 p_vout->output.i_chroma = FOURCC_BI_BITFIELDS; break;
417 intf_ErrMsg( "vout error: unknown screen depth" );
421 p_vout->output.i_width = p_vout->p_sys->i_width;
422 p_vout->output.i_height = p_vout->p_sys->i_height;
424 /* Assume we have square pixels */
425 p_vout->output.i_aspect = p_vout->p_sys->i_width
426 * VOUT_ASPECT_FACTOR / p_vout->p_sys->i_height;
429 /* Try to initialize up to MAX_DIRECTBUFFERS direct buffers */
430 while( I_OUTPUTPICTURES < MAX_DIRECTBUFFERS )
434 /* Find an empty picture slot */
435 for( i_index = 0 ; i_index < VOUT_MAX_PICTURES ; i_index++ )
437 if( p_vout->p_picture[ i_index ].i_status == FREE_PICTURE )
439 p_pic = p_vout->p_picture + i_index;
444 /* Allocate the picture */
445 if( p_pic == NULL || NewPicture( p_vout, p_pic ) )
450 p_pic->i_status = DESTROYED_PICTURE;
451 p_pic->i_type = DIRECT_PICTURE;
453 PP_OUTPUTPICTURE[ I_OUTPUTPICTURES ] = p_pic;
461 /*****************************************************************************
462 * vout_Render: render previously calculated output
463 *****************************************************************************/
464 static void vout_Render( vout_thread_t *p_vout, picture_t *p_pic )
469 /*****************************************************************************
470 * vout_Display: displays previously rendered output
471 *****************************************************************************
472 * This function sends the currently rendered image to X11 server.
473 * (The Xv extension takes care of "double-buffering".)
474 *****************************************************************************/
475 static void vout_Display( vout_thread_t *p_vout, picture_t *p_pic )
477 int i_width, i_height, i_x, i_y;
479 vout_PlacePicture( p_vout, p_vout->p_sys->i_width, p_vout->p_sys->i_height,
480 &i_x, &i_y, &i_width, &i_height );
483 /*****************************************************************************
484 * vout_Manage: handle X11 events
485 *****************************************************************************
486 * This function should be called regularly by video output thread. It manages
487 * X11 events and allows window resizing. It returns a non null value on
489 *****************************************************************************/
490 static int vout_Manage( vout_thread_t *p_vout )
492 XEvent xevent; /* X11 event */
493 boolean_t b_resized; /* window has been resized */
494 char i_key; /* ISO Latin-1 key */
497 /* Handle X11 events: ConfigureNotify events are parsed to know if the
498 * output window's size changed, MapNotify and UnmapNotify to know if the
499 * window is mapped (and if the display is useful), and ClientMessages
500 * to intercept window destruction requests */
503 while( XCheckWindowEvent( p_vout->p_sys->p_display, p_vout->p_sys->window,
504 StructureNotifyMask | KeyPressMask |
505 ButtonPressMask | ButtonReleaseMask |
506 PointerMotionMask | Button1MotionMask , &xevent )
509 /* ConfigureNotify event: prepare */
510 if( (xevent.type == ConfigureNotify)
511 && ((xevent.xconfigure.width != p_vout->p_sys->i_width)
512 || (xevent.xconfigure.height != p_vout->p_sys->i_height)) )
514 /* Update dimensions */
516 p_vout->i_changes |= VOUT_SIZE_CHANGE;
517 p_vout->p_sys->i_width = xevent.xconfigure.width;
518 p_vout->p_sys->i_height = xevent.xconfigure.height;
520 /* MapNotify event: change window status and disable screen saver */
521 else if( xevent.type == MapNotify)
523 if( (p_vout != NULL) && !p_vout->b_active )
525 DisableXScreenSaver( p_vout );
526 p_vout->b_active = 1;
529 /* UnmapNotify event: change window status and enable screen saver */
530 else if( xevent.type == UnmapNotify )
532 if( (p_vout != NULL) && p_vout->b_active )
534 EnableXScreenSaver( p_vout );
535 p_vout->b_active = 0;
539 else if( xevent.type == KeyPress )
541 /* We may have keys like F1 trough F12, ESC ... */
542 x_key_symbol = XKeycodeToKeysym( p_vout->p_sys->p_display,
543 xevent.xkey.keycode, 0 );
544 switch( x_key_symbol )
547 p_main->p_intf->b_die = 1;
550 p_main->p_intf->b_menu_change = 1;
565 input_Seek( p_input_bank->pp_input[0],
566 p_input_bank->pp_input[0]->stream.p_selected_area->i_start );
569 input_Seek( p_input_bank->pp_input[0],
570 p_input_bank->pp_input[0]->stream.p_selected_area->i_size );
579 input_SetStatus( p_input_bank->pp_input[0],
580 INPUT_STATUS_PAUSE );
585 * The reason why I use this instead of XK_0 is that
586 * with XLookupString, we don't have to care about
589 if( XLookupString( &xevent.xkey, &i_key, 1, NULL, NULL ) )
591 /* FIXME: handle stuff here */
596 p_main->p_intf->b_die = 1;
600 p_vout->i_changes |= VOUT_FULLSCREEN_CHANGE;
603 case '0': network_ChannelJoin( 0 ); break;
604 case '1': network_ChannelJoin( 1 ); break;
605 case '2': network_ChannelJoin( 2 ); break;
606 case '3': network_ChannelJoin( 3 ); break;
607 case '4': network_ChannelJoin( 4 ); break;
608 case '5': network_ChannelJoin( 5 ); break;
609 case '6': network_ChannelJoin( 6 ); break;
610 case '7': network_ChannelJoin( 7 ); break;
611 case '8': network_ChannelJoin( 8 ); break;
612 case '9': network_ChannelJoin( 9 ); break;
615 intf_DbgMsg( "vout: unhandled key '%c' (%i)",
616 (char)i_key, i_key );
624 else if( xevent.type == ButtonPress )
626 switch( ((XButtonEvent *)&xevent)->button )
629 /* In this part we will eventually manage
630 * clicks for DVD navigation for instance. For the
631 * moment just pause the stream. */
632 input_SetStatus( p_input_bank->pp_input[0],
633 INPUT_STATUS_PAUSE );
646 else if( xevent.type == ButtonRelease )
648 switch( ((XButtonEvent *)&xevent)->button )
651 /* FIXME: need locking ! */
652 p_main->p_intf->b_menu_change = 1;
657 else if( xevent.type == MotionNotify )
659 p_vout->p_sys->i_time_mouse_last_moved = mdate();
660 if( ! p_vout->p_sys->b_mouse_pointer_visible )
662 ToggleCursor( p_vout );
668 intf_WarnMsg( 3, "vout: unhandled event %d received", xevent.type );
672 /* ClientMessage event - only WM_PROTOCOLS with WM_DELETE_WINDOW data
673 * are handled - according to the man pages, the format is always 32
675 while( XCheckTypedEvent( p_vout->p_sys->p_display,
676 ClientMessage, &xevent ) )
678 if( (xevent.xclient.message_type == p_vout->p_sys->wm_protocols)
679 && (xevent.xclient.data.l[0] == p_vout->p_sys->wm_delete_window ) )
681 p_main->p_intf->b_die = 1;
685 intf_DbgMsg( "vout: unhandled ClientMessage received" );
692 if ( p_vout->i_changes & VOUT_FULLSCREEN_CHANGE )
694 ToggleFullScreen( p_vout );
695 p_vout->i_changes &= ~VOUT_FULLSCREEN_CHANGE;
702 if( p_vout->i_changes & VOUT_SIZE_CHANGE )
704 int i_width, i_height, i_x, i_y;
706 p_vout->i_changes &= ~VOUT_SIZE_CHANGE;
708 intf_WarnMsg( 3, "vout: video display resized (%dx%d)",
709 p_vout->p_sys->i_width,
710 p_vout->p_sys->i_height );
712 vout_PlacePicture( p_vout, p_vout->p_sys->i_width,
713 p_vout->p_sys->i_height,
714 &i_x, &i_y, &i_width, &i_height );
717 /* Autohide Cursour */
718 if( mdate() - p_vout->p_sys->i_time_mouse_last_moved > 2000000 )
720 /* Hide the mouse automatically */
721 if( p_vout->p_sys->b_mouse_pointer_visible )
723 ToggleCursor( p_vout );
730 /*****************************************************************************
731 * vout_End: terminate X11 video thread output method
732 *****************************************************************************
733 * Destroy the X11 XImages created by vout_Init. It is called at the end of
734 * the thread, but also each time the window is resized.
735 *****************************************************************************/
736 static void vout_End( vout_thread_t *p_vout )
740 /* Free the direct buffers we allocated */
741 for( i_index = I_OUTPUTPICTURES ; i_index ; )
744 FreePicture( p_vout, PP_OUTPUTPICTURE[ i_index ] );
748 /* following functions are local */
750 /*****************************************************************************
751 * CreateWindow: open and set-up X11 main window
752 *****************************************************************************/
753 static int CreateWindow( vout_thread_t *p_vout )
755 XSizeHints xsize_hints;
756 XSetWindowAttributes xwindow_attributes;
761 boolean_t b_configure_notify;
762 boolean_t b_map_notify;
764 /* Set main window's size */
765 if( p_vout->render.i_height * p_vout->render.i_aspect
766 >= p_vout->render.i_width * VOUT_ASPECT_FACTOR )
768 p_vout->p_sys->i_width = p_vout->render.i_height
769 * p_vout->render.i_aspect / VOUT_ASPECT_FACTOR;
770 p_vout->p_sys->i_height = p_vout->render.i_height;
774 p_vout->p_sys->i_width = p_vout->render.i_width;
775 p_vout->p_sys->i_height = p_vout->render.i_width
776 * VOUT_ASPECT_FACTOR / p_vout->render.i_aspect;
780 if( p_vout->p_sys->i_width <= 300 && p_vout->p_sys->i_height <= 300 )
782 p_vout->p_sys->i_width <<= 1;
783 p_vout->p_sys->i_height <<= 1;
785 else if( p_vout->p_sys->i_width <= 400
786 && p_vout->p_sys->i_height <= 400 )
788 p_vout->p_sys->i_width += p_vout->p_sys->i_width >> 1;
789 p_vout->p_sys->i_height += p_vout->p_sys->i_height >> 1;
793 /* Prepare window manager hints and properties */
794 xsize_hints.base_width = p_vout->p_sys->i_width;
795 xsize_hints.base_height = p_vout->p_sys->i_height;
796 xsize_hints.flags = PSize;
797 p_vout->p_sys->wm_protocols = XInternAtom( p_vout->p_sys->p_display,
798 "WM_PROTOCOLS", True );
799 p_vout->p_sys->wm_delete_window = XInternAtom( p_vout->p_sys->p_display,
800 "WM_DELETE_WINDOW", True );
802 /* Prepare window attributes */
803 xwindow_attributes.backing_store = Always; /* save the hidden part */
804 xwindow_attributes.background_pixel = BlackPixel(p_vout->p_sys->p_display,
805 p_vout->p_sys->i_screen);
806 xwindow_attributes.event_mask = ExposureMask | StructureNotifyMask;
809 /* Create the window and set hints - the window must receive
810 * ConfigureNotify events, and until it is displayed, Expose and
811 * MapNotify events. */
813 p_vout->p_sys->window =
814 XCreateWindow( p_vout->p_sys->p_display,
815 DefaultRootWindow( p_vout->p_sys->p_display ),
817 p_vout->p_sys->i_width,
818 p_vout->p_sys->i_height,
821 CWBackingStore | CWBackPixel | CWEventMask,
822 &xwindow_attributes );
824 /* Set window manager hints and properties: size hints, command,
825 * window's name, and accepted protocols */
826 XSetWMNormalHints( p_vout->p_sys->p_display, p_vout->p_sys->window,
828 XSetCommand( p_vout->p_sys->p_display, p_vout->p_sys->window,
829 p_main->ppsz_argv, p_main->i_argc );
830 XStoreName( p_vout->p_sys->p_display, p_vout->p_sys->window,
831 VOUT_TITLE " (XMGA output)"
834 if( (p_vout->p_sys->wm_protocols == None) /* use WM_DELETE_WINDOW */
835 || (p_vout->p_sys->wm_delete_window == None)
836 || !XSetWMProtocols( p_vout->p_sys->p_display, p_vout->p_sys->window,
837 &p_vout->p_sys->wm_delete_window, 1 ) )
839 /* WM_DELETE_WINDOW is not supported by window manager */
840 intf_Msg( "vout error: missing or bad window manager" );
843 /* Creation of a graphic context that doesn't generate a GraphicsExpose
844 * event when using functions like XCopyArea */
845 xgcvalues.graphics_exposures = False;
846 p_vout->p_sys->gc = XCreateGC( p_vout->p_sys->p_display,
847 p_vout->p_sys->window,
848 GCGraphicsExposures, &xgcvalues);
850 /* Send orders to server, and wait until window is displayed - three
851 * events must be received: a MapNotify event, an Expose event allowing
852 * drawing in the window, and a ConfigureNotify to get the window
853 * dimensions. Once those events have been received, only ConfigureNotify
854 * events need to be received. */
856 b_configure_notify = 0;
858 XMapWindow( p_vout->p_sys->p_display, p_vout->p_sys->window);
861 XNextEvent( p_vout->p_sys->p_display, &xevent);
862 if( (xevent.type == Expose)
863 && (xevent.xexpose.window == p_vout->p_sys->window) )
867 else if( (xevent.type == MapNotify)
868 && (xevent.xmap.window == p_vout->p_sys->window) )
872 else if( (xevent.type == ConfigureNotify)
873 && (xevent.xconfigure.window == p_vout->p_sys->window) )
875 b_configure_notify = 1;
876 p_vout->p_sys->i_width = xevent.xconfigure.width;
877 p_vout->p_sys->i_height = xevent.xconfigure.height;
879 } while( !( b_expose && b_configure_notify && b_map_notify ) );
881 XSelectInput( p_vout->p_sys->p_display, p_vout->p_sys->window,
882 StructureNotifyMask | KeyPressMask |
883 ButtonPressMask | ButtonReleaseMask |
886 /* If the cursor was formerly blank than blank it again */
887 if( !p_vout->p_sys->b_mouse_pointer_visible )
889 ToggleCursor( p_vout );
890 ToggleCursor( p_vout );
893 XSync( p_vout->p_sys->p_display, False );
895 /* At this stage, the window is open, displayed, and ready to
901 /*****************************************************************************
902 * DestroyWindow: destroy the window
903 *****************************************************************************
905 *****************************************************************************/
906 static void DestroyWindow( vout_thread_t *p_vout )
908 XSync( p_vout->p_sys->p_display, False );
910 XUnmapWindow( p_vout->p_sys->p_display, p_vout->p_sys->window );
911 XFreeGC( p_vout->p_sys->p_display, p_vout->p_sys->gc );
912 XDestroyWindow( p_vout->p_sys->p_display, p_vout->p_sys->window );
915 /*****************************************************************************
916 * NewPicture: allocate a picture
917 *****************************************************************************
918 * Returns 0 on success, -1 otherwise
919 *****************************************************************************/
920 static int NewPicture( vout_thread_t *p_vout, picture_t *p_pic )
922 /* We know the chroma, allocate a buffer which will be used
923 * directly by the decoder */
924 p_pic->p_sys = malloc( sizeof( picture_sys_t ) );
926 if( p_pic->p_sys == NULL )
933 switch( p_vout->output.i_chroma )
938 /* Unknown chroma, tell the guy to get lost */
939 free( p_pic->p_sys );
940 intf_ErrMsg( "vout error: never heard of chroma 0x%.8x (%4.4s)",
941 p_vout->output.i_chroma,
942 (char*)&p_vout->output.i_chroma );
950 /*****************************************************************************
951 * FreePicture: destroy a picture allocated with NewPicture
952 *****************************************************************************
953 * Destroy XImage AND associated data. If using Shm, detach shared memory
954 * segment from server and process, then free it. The XDestroyImage manpage
955 * says that both the image structure _and_ the data pointed to by the
956 * image structure are freed, so no need to free p_image->data.
957 *****************************************************************************/
958 static void FreePicture( vout_thread_t *p_vout, picture_t *p_pic )
961 XSync( p_vout->p_sys->p_display, False );
963 free( p_pic->p_sys );
966 /*****************************************************************************
967 * ToggleFullScreen: Enable or disable full screen mode
968 *****************************************************************************
969 * This function will switch between fullscreen and window mode.
971 *****************************************************************************/
972 static void ToggleFullScreen ( vout_thread_t *p_vout )
976 int i_xpos, i_ypos, i_width, i_height;
978 p_vout->b_fullscreen = !p_vout->b_fullscreen;
980 if( p_vout->b_fullscreen )
982 Window next_parent, parent, *p_dummy, dummy1;
983 unsigned int dummy2, dummy3;
985 intf_WarnMsg( 3, "vout: entering fullscreen mode" );
987 /* Save current window coordinates so they can be restored when
988 * we exit from fullscreen mode */
990 /* find the real parent, which means the which is a direct child of
992 next_parent = parent = p_vout->p_sys->window;
993 while( next_parent != DefaultRootWindow( p_vout->p_sys->p_display ) )
995 parent = next_parent;
996 XQueryTree( p_vout->p_sys->p_display,
1002 XFree((void *)p_dummy);
1005 XGetGeometry( p_vout->p_sys->p_display,
1006 p_vout->p_sys->window,
1010 &p_vout->p_sys->i_width_backup,
1011 &p_vout->p_sys->i_height_backup,
1014 XTranslateCoordinates( p_vout->p_sys->p_display,
1016 DefaultRootWindow( p_vout->p_sys->p_display ),
1019 &p_vout->p_sys->i_xpos_backup,
1020 &p_vout->p_sys->i_ypos_backup,
1023 mwmhints.flags = MWM_HINTS_DECORATIONS;
1024 mwmhints.decorations = 0;
1028 i_width = DisplayWidth( p_vout->p_sys->p_display,
1029 p_vout->p_sys->i_screen );
1030 i_height = DisplayHeight( p_vout->p_sys->p_display,
1031 p_vout->p_sys->i_screen );
1034 /* Being a transient window allows us to really be fullscreen (display
1035 * over the taskbar for instance) but then we end-up with the same
1036 * result as with the brute force method */
1037 XSetTransientForHint( p_vout->p_sys->p_display,
1038 p_vout->p_sys->window, None );
1043 intf_WarnMsg( 3, "vout: leaving fullscreen mode" );
1045 mwmhints.flags = MWM_HINTS_DECORATIONS;
1046 mwmhints.decorations = 1;
1048 i_xpos = p_vout->p_sys->i_xpos_backup;
1049 i_ypos = p_vout->p_sys->i_ypos_backup;
1050 i_width = p_vout->p_sys->i_width_backup;
1051 i_height = p_vout->p_sys->i_height_backup;
1054 /* To my knowledge there are two ways to create a borderless window.
1055 * There's the generic way which is to tell x to bypass the window manager,
1056 * but this creates problems with the focus of other applications.
1057 * The other way is to use the motif property "_MOTIF_WM_HINTS" which
1058 * luckily seems to be supported by most window managers.
1060 prop = XInternAtom( p_vout->p_sys->p_display, "_MOTIF_WM_HINTS",
1062 XChangeProperty( p_vout->p_sys->p_display, p_vout->p_sys->window,
1063 prop, prop, 32, PropModeReplace,
1064 (unsigned char *)&mwmhints,
1065 PROP_MWM_HINTS_ELEMENTS );
1066 #if 0 /* brute force way to remove decorations */
1067 XSetWindowAttributes attributes;
1068 attributes.override_redirect = True;
1069 XChangeWindowAttributes( p_vout->p_sys->p_display,
1070 p_vout->p_sys->window,
1075 /* We need to unmap and remap the window if we want the window
1076 * manager to take our changes into effect */
1077 XUnmapWindow( p_vout->p_sys->p_display, p_vout->p_sys->window);
1078 XMapRaised( p_vout->p_sys->p_display, p_vout->p_sys->window);
1079 XMoveResizeWindow( p_vout->p_sys->p_display,
1080 p_vout->p_sys->window,
1085 XFlush( p_vout->p_sys->p_display );
1088 /*****************************************************************************
1089 * EnableXScreenSaver: enable screen saver
1090 *****************************************************************************
1091 * This function enables the screen saver on a display after it has been
1092 * disabled by XDisableScreenSaver.
1093 * FIXME: what happens if multiple vlc sessions are running at the same
1095 *****************************************************************************/
1096 static void EnableXScreenSaver( vout_thread_t *p_vout )
1100 intf_DbgMsg( "vout: enabling screen saver" );
1101 XSetScreenSaver( p_vout->p_sys->p_display, p_vout->p_sys->i_ss_timeout,
1102 p_vout->p_sys->i_ss_interval,
1103 p_vout->p_sys->i_ss_blanking,
1104 p_vout->p_sys->i_ss_exposure );
1106 /* Restore DPMS settings */
1107 if( DPMSQueryExtension( p_vout->p_sys->p_display, &dummy, &dummy ) )
1109 if( p_vout->p_sys->b_ss_dpms )
1111 DPMSEnable( p_vout->p_sys->p_display );
1116 /*****************************************************************************
1117 * DisableXScreenSaver: disable screen saver
1118 *****************************************************************************
1119 * See XEnableXScreenSaver
1120 *****************************************************************************/
1121 static void DisableXScreenSaver( vout_thread_t *p_vout )
1125 /* Save screen saver informations */
1126 XGetScreenSaver( p_vout->p_sys->p_display, &p_vout->p_sys->i_ss_timeout,
1127 &p_vout->p_sys->i_ss_interval,
1128 &p_vout->p_sys->i_ss_blanking,
1129 &p_vout->p_sys->i_ss_exposure );
1131 /* Disable screen saver */
1132 intf_DbgMsg( "vout: disabling screen saver" );
1133 XSetScreenSaver( p_vout->p_sys->p_display, 0,
1134 p_vout->p_sys->i_ss_interval,
1135 p_vout->p_sys->i_ss_blanking,
1136 p_vout->p_sys->i_ss_exposure );
1139 if( DPMSQueryExtension( p_vout->p_sys->p_display, &dummy, &dummy ) )
1142 /* Save DPMS current state */
1143 DPMSInfo( p_vout->p_sys->p_display, &dummy,
1144 &p_vout->p_sys->b_ss_dpms );
1145 intf_DbgMsg( "vout: disabling DPMS" );
1146 DPMSDisable( p_vout->p_sys->p_display );
1150 /*****************************************************************************
1151 * CreateCursor: create a blank mouse pointer
1152 *****************************************************************************/
1153 static void CreateCursor( vout_thread_t *p_vout )
1155 XColor cursor_color;
1157 p_vout->p_sys->cursor_pixmap =
1158 XCreatePixmap( p_vout->p_sys->p_display,
1159 DefaultRootWindow( p_vout->p_sys->p_display ),
1162 XParseColor( p_vout->p_sys->p_display,
1163 XCreateColormap( p_vout->p_sys->p_display,
1165 p_vout->p_sys->p_display ),
1167 p_vout->p_sys->p_display,
1168 p_vout->p_sys->i_screen ),
1170 "black", &cursor_color );
1172 p_vout->p_sys->blank_cursor =
1173 XCreatePixmapCursor( p_vout->p_sys->p_display,
1174 p_vout->p_sys->cursor_pixmap,
1175 p_vout->p_sys->cursor_pixmap,
1176 &cursor_color, &cursor_color, 1, 1 );
1179 /*****************************************************************************
1180 * DestroyCursor: destroy the blank mouse pointer
1181 *****************************************************************************/
1182 static void DestroyCursor( vout_thread_t *p_vout )
1184 XFreePixmap( p_vout->p_sys->p_display, p_vout->p_sys->cursor_pixmap );
1187 /*****************************************************************************
1188 * ToggleCursor: hide or show the mouse pointer
1189 *****************************************************************************
1190 * This function hides the X pointer if it is visible by setting the pointer
1191 * sprite to a blank one. To show it again, we disable the sprite.
1192 *****************************************************************************/
1193 static void ToggleCursor( vout_thread_t *p_vout )
1195 if( p_vout->p_sys->b_mouse_pointer_visible )
1197 XDefineCursor( p_vout->p_sys->p_display,
1198 p_vout->p_sys->window,
1199 p_vout->p_sys->blank_cursor );
1200 p_vout->p_sys->b_mouse_pointer_visible = 0;
1204 XUndefineCursor( p_vout->p_sys->p_display, p_vout->p_sys->window );
1205 p_vout->p_sys->b_mouse_pointer_visible = 1;