1 /*****************************************************************************
2 * xmga.c : X11 MGA plugin for vlc
3 *****************************************************************************
4 * Copyright (C) 1998-2001 VideoLAN
5 * $Id: xmga.c,v 1.18 2002/07/20 18:01:43 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 void vout_getfunctions( function_list_t * );
64 static int vout_Create ( vout_thread_t * );
65 static void vout_Destroy ( vout_thread_t * );
66 static void vout_Render ( vout_thread_t *, picture_t * );
67 static void vout_Display ( vout_thread_t *, picture_t * );
68 static int vout_Manage ( vout_thread_t * );
69 static int vout_Init ( vout_thread_t * );
70 static void vout_End ( vout_thread_t * );
72 static int CreateWindow ( vout_thread_t * );
73 static void DestroyWindow ( vout_thread_t * );
75 static int NewPicture ( vout_thread_t *, picture_t * );
76 static void FreePicture ( vout_thread_t *, picture_t * );
78 static void ToggleFullScreen ( vout_thread_t * );
80 static void EnableXScreenSaver ( vout_thread_t * );
81 static void DisableXScreenSaver ( vout_thread_t * );
83 static void CreateCursor ( vout_thread_t * );
84 static void DestroyCursor ( vout_thread_t * );
85 static void ToggleCursor ( vout_thread_t * );
87 /*****************************************************************************
88 * Building configuration tree
89 *****************************************************************************/
91 #define ALT_FS_TEXT N_("alternate fullscreen method")
92 #define ALT_FS_LONGTEXT N_( \
93 "There are two ways to make a fullscreen window, unfortunately each one " \
94 "has its drawbacks.\n" \
95 "1) Let the window manager handle your fullscreen window (default). But " \
96 "things like taskbars will likely show on top of the video.\n" \
97 "2) Completly bypass the window manager, but then nothing will be able " \
98 "to show on top of the video.")
100 #define DISPLAY_TEXT N_("X11 display name")
101 #define DISPLAY_LONGTEXT N_( \
102 "Specify the X11 hardware display you want to use. By default vlc will " \
103 "use the value of the DISPLAY environment variable.")
106 ADD_CATEGORY_HINT( N_("Miscellaneous"), NULL )
107 ADD_STRING ( "xmga-display", NULL, NULL, DISPLAY_TEXT, DISPLAY_LONGTEXT )
108 ADD_BOOL ( "xmga-altfullscreen", 0, NULL, ALT_FS_TEXT, ALT_FS_LONGTEXT )
112 SET_DESCRIPTION( _("X11 MGA module") )
113 ADD_CAPABILITY( VOUT, 60 )
116 MODULE_ACTIVATE_START
117 vout_getfunctions( &p_module->p_functions->vout );
120 MODULE_DEACTIVATE_START
121 MODULE_DEACTIVATE_STOP
123 /*****************************************************************************
124 * vout_sys_t: video output method descriptor
125 *****************************************************************************
126 * This structure is part of the video output thread descriptor.
127 * It describes the X11 and XVideo specific properties of an output thread.
128 *****************************************************************************/
131 /* Internal settings and properties */
132 Display * p_display; /* display pointer */
134 Visual * p_visual; /* visual pointer */
135 int i_screen; /* screen number */
136 Window window; /* root window */
137 GC gc; /* graphic context instance handler */
139 vlc_bool_t b_shm; /* shared memory extension flag */
141 #ifdef MODULE_NAME_IS_xvideo
142 Window yuv_window; /* sub-window for displaying yuv video
146 Colormap colormap; /* colormap used (8bpp only) */
149 int i_bytes_per_pixel;
150 int i_bytes_per_line;
156 /* X11 generic properties */
158 Atom wm_delete_window;
160 int i_width; /* width of main window */
161 int i_height; /* height of main window */
162 vlc_bool_t b_altfullscreen; /* which fullscreen method */
164 /* Backup of window position and size before fullscreen switch */
169 int i_width_backup_2;
170 int i_height_backup_2;
174 /* Screen saver properties */
175 int i_ss_timeout; /* timeout */
176 int i_ss_interval; /* interval between changes */
177 int i_ss_blanking; /* blanking mode */
178 int i_ss_exposure; /* exposure mode */
179 BOOL b_ss_dpms; /* DPMS mode */
181 /* Mouse pointer properties */
182 vlc_bool_t b_mouse_pointer_visible;
183 mtime_t i_time_mouse_last_moved; /* used to auto-hide pointer*/
184 Cursor blank_cursor; /* the hidden cursor */
185 mtime_t i_time_button_last_pressed; /* to track dbl-clicks */
186 Pixmap cursor_pixmap;
189 /*****************************************************************************
190 * mwmhints_t: window manager hints
191 *****************************************************************************
192 * Fullscreen needs to be able to hide the wm decorations so we provide
193 * this structure to make it easier.
194 *****************************************************************************/
195 #define MWM_HINTS_DECORATIONS (1L << 1)
196 #define PROP_MWM_HINTS_ELEMENTS 5
197 typedef struct mwmhints_t
206 /*****************************************************************************
208 *****************************************************************************/
209 #ifdef MODULE_NAME_IS_xvideo
210 # define MAX_DIRECTBUFFERS 5
212 # define MAX_DIRECTBUFFERS 2
215 /*****************************************************************************
216 * Functions exported as capabilities. They are declared as static so that
217 * we don't pollute the namespace too much.
218 *****************************************************************************/
219 static void vout_getfunctions( function_list_t * p_function_list )
221 p_function_list->functions.vout.pf_create = vout_Create;
222 p_function_list->functions.vout.pf_init = vout_Init;
223 p_function_list->functions.vout.pf_end = vout_End;
224 p_function_list->functions.vout.pf_destroy = vout_Destroy;
225 p_function_list->functions.vout.pf_manage = vout_Manage;
226 p_function_list->functions.vout.pf_render = vout_Render;
227 p_function_list->functions.vout.pf_display = vout_Display;
230 /*****************************************************************************
231 * vout_Create: allocate X11 video thread output method
232 *****************************************************************************
233 * This function allocate and initialize a X11 vout method. It uses some of the
234 * vout properties to choose the window size, and change them according to the
235 * actual properties of the display.
236 *****************************************************************************/
237 static int vout_Create( vout_thread_t *p_vout )
241 /* Allocate structure */
242 p_vout->p_sys = malloc( sizeof( vout_sys_t ) );
243 if( p_vout->p_sys == NULL )
245 msg_Err( p_vout, "out of memory" );
249 /* Open display, unsing the "display" config variable or the DISPLAY
250 * environment variable */
251 psz_display = config_GetPsz( p_vout, "xmga-display" );
252 p_vout->p_sys->p_display = XOpenDisplay( psz_display );
254 if( p_vout->p_sys->p_display == NULL ) /* error */
256 msg_Err( p_vout, "cannot open display %s",
257 XDisplayName( psz_display ) );
258 free( p_vout->p_sys );
259 if( psz_display ) free( psz_display );
262 if( psz_display ) free( psz_display );
264 p_vout->p_sys->i_screen = DefaultScreen( p_vout->p_sys->p_display );
266 /* Create blank cursor (for mouse cursor autohiding) */
267 p_vout->p_sys->b_mouse_pointer_visible = 1;
268 CreateCursor( p_vout );
270 /* Spawn base window - this window will include the video output window,
271 * but also command buttons, subtitles and other indicators */
272 if( CreateWindow( p_vout ) )
274 msg_Err( p_vout, "cannot create X11 window" );
275 DestroyCursor( p_vout );
276 XCloseDisplay( p_vout->p_sys->p_display );
277 free( p_vout->p_sys );
281 /* Disable screen saver */
282 DisableXScreenSaver( p_vout );
285 p_vout->p_sys->b_altfullscreen = 0;
290 /*****************************************************************************
291 * vout_Destroy: destroy X11 video thread output method
292 *****************************************************************************
293 * Terminate an output method created by vout_CreateOutputMethod
294 *****************************************************************************/
295 static void vout_Destroy( vout_thread_t *p_vout )
297 /* Restore cursor if it was blanked */
298 if( !p_vout->p_sys->b_mouse_pointer_visible )
300 ToggleCursor( p_vout );
303 DestroyCursor( p_vout );
304 EnableXScreenSaver( p_vout );
305 DestroyWindow( p_vout );
307 XCloseDisplay( p_vout->p_sys->p_display );
309 /* Destroy structure */
310 free( p_vout->p_sys );
313 /*****************************************************************************
314 * vout_Init: initialize X11 video thread output method
315 *****************************************************************************
316 * This function create the XImages needed by the output thread. It is called
317 * at the beginning of the thread, but also each time the window is resized.
318 *****************************************************************************/
319 static int vout_Init( vout_thread_t *p_vout )
324 I_OUTPUTPICTURES = 0;
326 #ifdef MODULE_NAME_IS_xvideo
327 /* Initialize the output structure; we already found an XVideo port,
328 * and the corresponding chroma we will be using. Since we can
329 * arbitrary scale, stick to the coordinates and aspect. */
330 p_vout->output.i_width = p_vout->render.i_width;
331 p_vout->output.i_height = p_vout->render.i_height;
332 p_vout->output.i_aspect = p_vout->render.i_aspect;
335 /* Initialize the output structure: RGB with square pixels, whatever
336 * the input format is, since it's the only format we know */
337 switch( p_vout->p_sys->i_screen_depth )
339 case 8: /* FIXME: set the palette */
340 p_vout->output.i_chroma = FOURCC_RGB2; break;
342 p_vout->output.i_chroma = FOURCC_RV15; break;
344 p_vout->output.i_chroma = FOURCC_RV16; break;
346 p_vout->output.i_chroma = FOURCC_RV24; break;
348 p_vout->output.i_chroma = FOURCC_RV32; break;
350 msg_Err( p_vout, "unknown screen depth %i",
351 p_vout->p_sys->i_screen_depth );
355 p_vout->output.i_width = p_vout->p_sys->i_width;
356 p_vout->output.i_height = p_vout->p_sys->i_height;
358 /* Assume we have square pixels */
359 p_vout->output.i_aspect = p_vout->p_sys->i_width
360 * VOUT_ASPECT_FACTOR / p_vout->p_sys->i_height;
363 /* Try to initialize up to MAX_DIRECTBUFFERS direct buffers */
364 while( I_OUTPUTPICTURES < MAX_DIRECTBUFFERS )
368 /* Find an empty picture slot */
369 for( i_index = 0 ; i_index < VOUT_MAX_PICTURES ; i_index++ )
371 if( p_vout->p_picture[ i_index ].i_status == FREE_PICTURE )
373 p_pic = p_vout->p_picture + i_index;
378 /* Allocate the picture */
379 if( p_pic == NULL || NewPicture( p_vout, p_pic ) )
384 p_pic->i_status = DESTROYED_PICTURE;
385 p_pic->i_type = DIRECT_PICTURE;
387 PP_OUTPUTPICTURE[ I_OUTPUTPICTURES ] = p_pic;
395 /*****************************************************************************
396 * vout_Render: render previously calculated output
397 *****************************************************************************/
398 static void vout_Render( vout_thread_t *p_vout, picture_t *p_pic )
403 /*****************************************************************************
404 * vout_Display: displays previously rendered output
405 *****************************************************************************
406 * This function sends the currently rendered image to X11 server.
407 * (The Xv extension takes care of "double-buffering".)
408 *****************************************************************************/
409 static void vout_Display( vout_thread_t *p_vout, picture_t *p_pic )
411 int i_width, i_height, i_x, i_y;
413 vout_PlacePicture( p_vout, p_vout->p_sys->i_width, p_vout->p_sys->i_height,
414 &i_x, &i_y, &i_width, &i_height );
417 /*****************************************************************************
418 * vout_Manage: handle X11 events
419 *****************************************************************************
420 * This function should be called regularly by video output thread. It manages
421 * X11 events and allows window resizing. It returns a non null value on
423 *****************************************************************************/
424 static int vout_Manage( vout_thread_t *p_vout )
426 XEvent xevent; /* X11 event */
427 vlc_bool_t b_resized; /* window has been resized */
428 char i_key; /* ISO Latin-1 key */
431 /* Handle X11 events: ConfigureNotify events are parsed to know if the
432 * output window's size changed, MapNotify and UnmapNotify to know if the
433 * window is mapped (and if the display is useful), and ClientMessages
434 * to intercept window destruction requests */
437 while( XCheckWindowEvent( p_vout->p_sys->p_display, p_vout->p_sys->window,
438 StructureNotifyMask | KeyPressMask |
439 ButtonPressMask | ButtonReleaseMask |
440 PointerMotionMask | Button1MotionMask , &xevent )
443 /* ConfigureNotify event: prepare */
444 if( (xevent.type == ConfigureNotify)
445 && ((xevent.xconfigure.width != p_vout->p_sys->i_width)
446 || (xevent.xconfigure.height != p_vout->p_sys->i_height)) )
448 /* Update dimensions */
450 p_vout->i_changes |= VOUT_SIZE_CHANGE;
451 p_vout->p_sys->i_width = xevent.xconfigure.width;
452 p_vout->p_sys->i_height = xevent.xconfigure.height;
455 else if( xevent.type == KeyPress )
457 /* We may have keys like F1 trough F12, ESC ... */
458 x_key_symbol = XKeycodeToKeysym( p_vout->p_sys->p_display,
459 xevent.xkey.keycode, 0 );
460 switch( x_key_symbol )
463 p_vout->p_vlc->b_die = 1;
466 p_vout->p_vlc->p_intf->b_menu_change = 1;
469 input_Seek( p_vout, -5, INPUT_SEEK_SECONDS | INPUT_SEEK_CUR );
472 input_Seek( p_vout, 5, INPUT_SEEK_SECONDS | INPUT_SEEK_CUR );
475 input_Seek( p_vout, 60, INPUT_SEEK_SECONDS | INPUT_SEEK_CUR );
478 input_Seek( p_vout, -60, INPUT_SEEK_SECONDS | INPUT_SEEK_CUR );
481 input_Seek( p_vout, 0, INPUT_SEEK_BYTES | INPUT_SEEK_SET );
484 input_Seek( p_vout, 0, INPUT_SEEK_BYTES | INPUT_SEEK_END );
487 input_Seek( p_vout, 900, INPUT_SEEK_SECONDS | INPUT_SEEK_CUR );
490 input_Seek( p_vout, -900, INPUT_SEEK_SECONDS | INPUT_SEEK_CUR );
493 input_SetStatus( p_input_bank->pp_input[0],
494 INPUT_STATUS_PAUSE );
497 case XK_F1: network_ChannelJoin( p_vout, 1 ); break;
498 case XK_F2: network_ChannelJoin( p_vout, 2 ); break;
499 case XK_F3: network_ChannelJoin( p_vout, 3 ); break;
500 case XK_F4: network_ChannelJoin( p_vout, 4 ); break;
501 case XK_F5: network_ChannelJoin( p_vout, 5 ); break;
502 case XK_F6: network_ChannelJoin( p_vout, 6 ); break;
503 case XK_F7: network_ChannelJoin( p_vout, 7 ); break;
504 case XK_F8: network_ChannelJoin( p_vout, 8 ); break;
505 case XK_F9: network_ChannelJoin( p_vout, 9 ); break;
506 case XK_F10: network_ChannelJoin( p_vout, 10 ); break;
507 case XK_F11: network_ChannelJoin( p_vout, 11 ); break;
508 case XK_F12: network_ChannelJoin( p_vout, 12 ); break;
512 * The reason why I use this instead of XK_0 is that
513 * with XLookupString, we don't have to care about
516 if( XLookupString( &xevent.xkey, &i_key, 1, NULL, NULL ) )
518 /* FIXME: handle stuff here */
523 p_vout->p_vlc->b_die = 1;
527 p_vout->i_changes |= VOUT_FULLSCREEN_CHANGE;
538 else if( xevent.type == ButtonPress )
540 switch( ((XButtonEvent *)&xevent)->button )
543 /* In this part we will eventually manage
544 * clicks for DVD navigation for instance. */
546 /* detect double-clicks */
547 if( ( ((XButtonEvent *)&xevent)->time -
548 p_vout->p_sys->i_time_button_last_pressed ) < 300 )
550 p_vout->i_changes |= VOUT_FULLSCREEN_CHANGE;
553 p_vout->p_sys->i_time_button_last_pressed =
554 ((XButtonEvent *)&xevent)->time;
558 input_Seek( p_vout, 15, INPUT_SEEK_SECONDS | INPUT_SEEK_CUR );
562 input_Seek( p_vout, -15, INPUT_SEEK_SECONDS | INPUT_SEEK_CUR );
567 else if( xevent.type == ButtonRelease )
569 switch( ((XButtonEvent *)&xevent)->button )
572 /* FIXME: need locking ! */
573 p_vout->p_vlc->p_intf->b_menu_change = 1;
578 else if( xevent.type == MotionNotify )
580 p_vout->p_sys->i_time_mouse_last_moved = mdate();
581 if( ! p_vout->p_sys->b_mouse_pointer_visible )
583 ToggleCursor( p_vout );
589 msg_Warn( p_vout, "unhandled event %d received", xevent.type );
593 /* ClientMessage event - only WM_PROTOCOLS with WM_DELETE_WINDOW data
594 * are handled - according to the man pages, the format is always 32
596 while( XCheckTypedEvent( p_vout->p_sys->p_display,
597 ClientMessage, &xevent ) )
599 if( (xevent.xclient.message_type == p_vout->p_sys->wm_protocols)
600 && (xevent.xclient.data.l[0] == p_vout->p_sys->wm_delete_window ) )
602 p_vout->p_vlc->b_die = 1;
609 if ( p_vout->i_changes & VOUT_FULLSCREEN_CHANGE )
611 ToggleFullScreen( p_vout );
612 p_vout->i_changes &= ~VOUT_FULLSCREEN_CHANGE;
619 if( p_vout->i_changes & VOUT_SIZE_CHANGE )
621 int i_width, i_height, i_x, i_y;
623 p_vout->i_changes &= ~VOUT_SIZE_CHANGE;
625 msg_Dbg( p_vout, "video display resized (%dx%d)",
626 p_vout->p_sys->i_width, p_vout->p_sys->i_height );
628 vout_PlacePicture( p_vout, p_vout->p_sys->i_width,
629 p_vout->p_sys->i_height,
630 &i_x, &i_y, &i_width, &i_height );
633 /* Autohide Cursour */
634 if( mdate() - p_vout->p_sys->i_time_mouse_last_moved > 2000000 )
636 /* Hide the mouse automatically */
637 if( p_vout->p_sys->b_mouse_pointer_visible )
639 ToggleCursor( p_vout );
646 /*****************************************************************************
647 * vout_End: terminate X11 video thread output method
648 *****************************************************************************
649 * Destroy the X11 XImages created by vout_Init. It is called at the end of
650 * the thread, but also each time the window is resized.
651 *****************************************************************************/
652 static void vout_End( vout_thread_t *p_vout )
656 /* Free the direct buffers we allocated */
657 for( i_index = I_OUTPUTPICTURES ; i_index ; )
660 FreePicture( p_vout, PP_OUTPUTPICTURE[ i_index ] );
664 /* following functions are local */
666 /*****************************************************************************
667 * CreateWindow: open and set-up X11 main window
668 *****************************************************************************/
669 static int CreateWindow( vout_thread_t *p_vout )
671 XSizeHints xsize_hints;
672 XSetWindowAttributes xwindow_attributes;
677 vlc_bool_t b_configure_notify;
678 vlc_bool_t b_map_notify;
680 /* Set main window's size */
681 p_vout->p_sys->i_width = p_vout->i_window_width;
682 p_vout->p_sys->i_height = p_vout->i_window_height;
684 /* Prepare window manager hints and properties */
685 xsize_hints.base_width = p_vout->p_sys->i_width;
686 xsize_hints.base_height = p_vout->p_sys->i_height;
687 xsize_hints.flags = PSize;
688 p_vout->p_sys->wm_protocols = XInternAtom( p_vout->p_sys->p_display,
689 "WM_PROTOCOLS", True );
690 p_vout->p_sys->wm_delete_window = XInternAtom( p_vout->p_sys->p_display,
691 "WM_DELETE_WINDOW", True );
693 /* Prepare window attributes */
694 xwindow_attributes.backing_store = Always; /* save the hidden part */
695 xwindow_attributes.background_pixel = BlackPixel(p_vout->p_sys->p_display,
696 p_vout->p_sys->i_screen);
697 xwindow_attributes.event_mask = ExposureMask | StructureNotifyMask;
700 /* Create the window and set hints - the window must receive
701 * ConfigureNotify events, and until it is displayed, Expose and
702 * MapNotify events. */
704 p_vout->p_sys->window =
705 XCreateWindow( p_vout->p_sys->p_display,
706 DefaultRootWindow( p_vout->p_sys->p_display ),
708 p_vout->p_sys->i_width,
709 p_vout->p_sys->i_height,
712 CWBackingStore | CWBackPixel | CWEventMask,
713 &xwindow_attributes );
715 /* Set window manager hints and properties: size hints, command,
716 * window's name, and accepted protocols */
717 XSetWMNormalHints( p_vout->p_sys->p_display, p_vout->p_sys->window,
719 XSetCommand( p_vout->p_sys->p_display, p_vout->p_sys->window,
720 p_vout->p_vlc->ppsz_argv, p_vout->p_vlc->i_argc );
721 XStoreName( p_vout->p_sys->p_display, p_vout->p_sys->window,
722 VOUT_TITLE " (XMGA output)"
725 if( (p_vout->p_sys->wm_protocols == None) /* use WM_DELETE_WINDOW */
726 || (p_vout->p_sys->wm_delete_window == None)
727 || !XSetWMProtocols( p_vout->p_sys->p_display, p_vout->p_sys->window,
728 &p_vout->p_sys->wm_delete_window, 1 ) )
730 /* WM_DELETE_WINDOW is not supported by window manager */
731 msg_Err( p_vout, "missing or bad window manager" );
734 /* Creation of a graphic context that doesn't generate a GraphicsExpose
735 * event when using functions like XCopyArea */
736 xgcvalues.graphics_exposures = False;
737 p_vout->p_sys->gc = XCreateGC( p_vout->p_sys->p_display,
738 p_vout->p_sys->window,
739 GCGraphicsExposures, &xgcvalues);
741 /* Send orders to server, and wait until window is displayed - three
742 * events must be received: a MapNotify event, an Expose event allowing
743 * drawing in the window, and a ConfigureNotify to get the window
744 * dimensions. Once those events have been received, only ConfigureNotify
745 * events need to be received. */
747 b_configure_notify = 0;
749 XMapWindow( p_vout->p_sys->p_display, p_vout->p_sys->window);
752 XNextEvent( p_vout->p_sys->p_display, &xevent);
753 if( (xevent.type == Expose)
754 && (xevent.xexpose.window == p_vout->p_sys->window) )
758 else if( (xevent.type == MapNotify)
759 && (xevent.xmap.window == p_vout->p_sys->window) )
763 else if( (xevent.type == ConfigureNotify)
764 && (xevent.xconfigure.window == p_vout->p_sys->window) )
766 b_configure_notify = 1;
767 p_vout->p_sys->i_width = xevent.xconfigure.width;
768 p_vout->p_sys->i_height = xevent.xconfigure.height;
770 } while( !( b_expose && b_configure_notify && b_map_notify ) );
772 XSelectInput( p_vout->p_sys->p_display, p_vout->p_sys->window,
773 StructureNotifyMask | KeyPressMask |
774 ButtonPressMask | ButtonReleaseMask |
777 /* If the cursor was formerly blank than blank it again */
778 if( !p_vout->p_sys->b_mouse_pointer_visible )
780 ToggleCursor( p_vout );
781 ToggleCursor( p_vout );
784 XSync( p_vout->p_sys->p_display, False );
786 /* At this stage, the window is open, displayed, and ready to
792 /*****************************************************************************
793 * DestroyWindow: destroy the window
794 *****************************************************************************
796 *****************************************************************************/
797 static void DestroyWindow( vout_thread_t *p_vout )
799 XSync( p_vout->p_sys->p_display, False );
801 XUnmapWindow( p_vout->p_sys->p_display, p_vout->p_sys->window );
802 XFreeGC( p_vout->p_sys->p_display, p_vout->p_sys->gc );
803 XDestroyWindow( p_vout->p_sys->p_display, p_vout->p_sys->window );
806 /*****************************************************************************
807 * NewPicture: allocate a picture
808 *****************************************************************************
809 * Returns 0 on success, -1 otherwise
810 *****************************************************************************/
811 static int NewPicture( vout_thread_t *p_vout, picture_t *p_pic )
813 /* We know the chroma, allocate a buffer which will be used
814 * directly by the decoder */
815 switch( p_vout->output.i_chroma )
820 /* Unknown chroma, tell the guy to get lost */
821 msg_Err( p_vout, "never heard of chroma 0x%.8x (%4.4s)",
822 p_vout->output.i_chroma, (char*)&p_vout->output.i_chroma );
830 /*****************************************************************************
831 * FreePicture: destroy a picture allocated with NewPicture
832 *****************************************************************************
833 * Destroy XImage AND associated data. If using Shm, detach shared memory
834 * segment from server and process, then free it. The XDestroyImage manpage
835 * says that both the image structure _and_ the data pointed to by the
836 * image structure are freed, so no need to free p_image->data.
837 *****************************************************************************/
838 static void FreePicture( vout_thread_t *p_vout, picture_t *p_pic )
840 XSync( p_vout->p_sys->p_display, False );
843 /*****************************************************************************
844 * ToggleFullScreen: Enable or disable full screen mode
845 *****************************************************************************
846 * This function will switch between fullscreen and window mode.
848 *****************************************************************************/
849 static void ToggleFullScreen ( vout_thread_t *p_vout )
853 int i_xpos, i_ypos, i_width, i_height;
855 XSetWindowAttributes attributes;
857 p_vout->b_fullscreen = !p_vout->b_fullscreen;
859 if( p_vout->b_fullscreen )
861 Window next_parent, parent, *p_dummy, dummy1;
862 unsigned int dummy2, dummy3;
864 msg_Dbg( p_vout, "entering fullscreen mode" );
866 /* Only check the fullscreen method when we actually go fullscreen,
867 * because to go back to window mode we need to know in which
868 * fullscreen mode we where */
869 p_vout->p_sys->b_altfullscreen = config_GetInt( p_vout,
870 "xmga-altfullscreen" );
872 /* Save current window coordinates so they can be restored when
873 * we exit from fullscreen mode. This is the tricky part because
874 * this heavily depends on the behaviour of the window manager.
875 * When you use XMoveWindow some window managers will adjust the top
876 * of the window to the coordinates you gave, but others will instead
877 * adjust the top of the client area to the coordinates
878 * (don't forget windows have decorations). */
880 /* First, get the position and size of the client area */
881 XGetGeometry( p_vout->p_sys->p_display,
882 p_vout->p_sys->window,
886 &p_vout->p_sys->i_width_backup_2,
887 &p_vout->p_sys->i_height_backup_2,
889 XTranslateCoordinates( p_vout->p_sys->p_display,
890 p_vout->p_sys->window,
891 DefaultRootWindow( p_vout->p_sys->p_display ),
894 &p_vout->p_sys->i_xpos_backup_2,
895 &p_vout->p_sys->i_ypos_backup_2,
898 /* Then try to get the position and size of the whole window */
900 /* find the real parent of our window (created by the window manager),
901 * the one which is a direct child of the root window */
902 next_parent = parent = p_vout->p_sys->window;
903 while( next_parent != DefaultRootWindow( p_vout->p_sys->p_display ) )
905 parent = next_parent;
906 XQueryTree( p_vout->p_sys->p_display,
912 XFree((void *)p_dummy);
915 XGetGeometry( p_vout->p_sys->p_display,
916 p_vout->p_sys->window,
920 &p_vout->p_sys->i_width_backup,
921 &p_vout->p_sys->i_height_backup,
924 XTranslateCoordinates( p_vout->p_sys->p_display,
926 DefaultRootWindow( p_vout->p_sys->p_display ),
929 &p_vout->p_sys->i_xpos_backup,
930 &p_vout->p_sys->i_ypos_backup,
933 /* fullscreen window size and position */
936 i_width = DisplayWidth( p_vout->p_sys->p_display,
937 p_vout->p_sys->i_screen );
938 i_height = DisplayHeight( p_vout->p_sys->p_display,
939 p_vout->p_sys->i_screen );
944 msg_Dbg( p_vout, "leaving fullscreen mode" );
946 i_xpos = p_vout->p_sys->i_xpos_backup;
947 i_ypos = p_vout->p_sys->i_ypos_backup;
948 i_width = p_vout->p_sys->i_width_backup;
949 i_height = p_vout->p_sys->i_height_backup;
952 /* To my knowledge there are two ways to create a borderless window.
953 * There's the generic way which is to tell x to bypass the window manager,
954 * but this creates problems with the focus of other applications.
955 * The other way is to use the motif property "_MOTIF_WM_HINTS" which
956 * luckily seems to be supported by most window managers.
958 if( !p_vout->p_sys->b_altfullscreen )
960 mwmhints.flags = MWM_HINTS_DECORATIONS;
961 mwmhints.decorations = !p_vout->b_fullscreen;
963 prop = XInternAtom( p_vout->p_sys->p_display, "_MOTIF_WM_HINTS",
965 XChangeProperty( p_vout->p_sys->p_display, p_vout->p_sys->window,
966 prop, prop, 32, PropModeReplace,
967 (unsigned char *)&mwmhints,
968 PROP_MWM_HINTS_ELEMENTS );
972 /* brute force way to remove decorations */
973 attributes.override_redirect = p_vout->b_fullscreen;
974 XChangeWindowAttributes( p_vout->p_sys->p_display,
975 p_vout->p_sys->window,
980 /* We need to unmap and remap the window if we want the window
981 * manager to take our changes into effect */
982 XUnmapWindow( p_vout->p_sys->p_display, p_vout->p_sys->window);
984 XWindowEvent( p_vout->p_sys->p_display, p_vout->p_sys->window,
985 StructureNotifyMask, &xevent );
986 while( xevent.type != UnmapNotify )
987 XWindowEvent( p_vout->p_sys->p_display, p_vout->p_sys->window,
988 StructureNotifyMask, &xevent );
990 XMapRaised( p_vout->p_sys->p_display, p_vout->p_sys->window);
992 while( xevent.type != MapNotify )
993 XWindowEvent( p_vout->p_sys->p_display, p_vout->p_sys->window,
994 StructureNotifyMask, &xevent );
996 XMoveResizeWindow( p_vout->p_sys->p_display,
997 p_vout->p_sys->window,
1003 /* Purge all ConfigureNotify events, this is needed to fix a bug where we
1004 * would lose the original size of the window */
1005 while( xevent.type != ConfigureNotify )
1006 XWindowEvent( p_vout->p_sys->p_display, p_vout->p_sys->window,
1007 StructureNotifyMask, &xevent );
1008 while( XCheckWindowEvent( p_vout->p_sys->p_display, p_vout->p_sys->window,
1009 StructureNotifyMask, &xevent ) );
1012 /* We need to check that the window was really restored where we wanted */
1013 if( !p_vout->b_fullscreen )
1016 unsigned int dummy2, dummy3, dummy4, dummy5;
1018 /* Check the position */
1019 XTranslateCoordinates( p_vout->p_sys->p_display,
1020 p_vout->p_sys->window,
1021 DefaultRootWindow( p_vout->p_sys->p_display ),
1027 if( dummy2 != p_vout->p_sys->i_xpos_backup_2 ||
1028 dummy3 != p_vout->p_sys->i_ypos_backup_2 )
1030 /* Ok it didn't work... second try */
1032 XMoveWindow( p_vout->p_sys->p_display,
1033 p_vout->p_sys->window,
1034 p_vout->p_sys->i_xpos_backup_2,
1035 p_vout->p_sys->i_ypos_backup_2 );
1037 /* Purge all ConfigureNotify events, this is needed to fix a bug
1038 * where we would lose the original size of the window */
1039 XWindowEvent( p_vout->p_sys->p_display, p_vout->p_sys->window,
1040 StructureNotifyMask, &xevent );
1041 while( xevent.type != ConfigureNotify )
1042 XWindowEvent( p_vout->p_sys->p_display, p_vout->p_sys->window,
1043 StructureNotifyMask, &xevent );
1044 while( XCheckWindowEvent( p_vout->p_sys->p_display,
1045 p_vout->p_sys->window,
1046 StructureNotifyMask, &xevent ) );
1049 /* Check the size */
1050 XGetGeometry( p_vout->p_sys->p_display,
1051 p_vout->p_sys->window,
1059 if( dummy4 != p_vout->p_sys->i_width_backup_2 ||
1060 dummy5 != p_vout->p_sys->i_height_backup_2 )
1062 /* Ok it didn't work... third try */
1064 XResizeWindow( p_vout->p_sys->p_display,
1065 p_vout->p_sys->window,
1066 p_vout->p_sys->i_width_backup_2,
1067 p_vout->p_sys->i_height_backup_2 );
1069 /* Purge all ConfigureNotify events, this is needed to fix a bug
1070 * where we would lose the original size of the window */
1071 XWindowEvent( p_vout->p_sys->p_display, p_vout->p_sys->window,
1072 StructureNotifyMask, &xevent );
1073 while( xevent.type != ConfigureNotify )
1074 XWindowEvent( p_vout->p_sys->p_display, p_vout->p_sys->window,
1075 StructureNotifyMask, &xevent );
1076 while( XCheckWindowEvent( p_vout->p_sys->p_display,
1077 p_vout->p_sys->window,
1078 StructureNotifyMask, &xevent ) );
1082 if( p_vout->p_sys->b_altfullscreen )
1083 XSetInputFocus(p_vout->p_sys->p_display,
1084 p_vout->p_sys->window,
1088 /* signal that the size needs to be updated */
1089 p_vout->p_sys->i_width = i_width;
1090 p_vout->p_sys->i_height = i_height;
1091 p_vout->i_changes |= VOUT_SIZE_CHANGE;
1095 /*****************************************************************************
1096 * EnableXScreenSaver: enable screen saver
1097 *****************************************************************************
1098 * This function enables the screen saver on a display after it has been
1099 * disabled by XDisableScreenSaver.
1100 * FIXME: what happens if multiple vlc sessions are running at the same
1102 *****************************************************************************/
1103 static void EnableXScreenSaver( vout_thread_t *p_vout )
1107 XSetScreenSaver( 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 /* Restore DPMS settings */
1113 if( DPMSQueryExtension( p_vout->p_sys->p_display, &dummy, &dummy ) )
1115 if( p_vout->p_sys->b_ss_dpms )
1117 DPMSEnable( p_vout->p_sys->p_display );
1122 /*****************************************************************************
1123 * DisableXScreenSaver: disable screen saver
1124 *****************************************************************************
1125 * See XEnableXScreenSaver
1126 *****************************************************************************/
1127 static void DisableXScreenSaver( vout_thread_t *p_vout )
1131 /* Save screen saver informations */
1132 XGetScreenSaver( p_vout->p_sys->p_display, &p_vout->p_sys->i_ss_timeout,
1133 &p_vout->p_sys->i_ss_interval,
1134 &p_vout->p_sys->i_ss_blanking,
1135 &p_vout->p_sys->i_ss_exposure );
1137 /* Disable screen saver */
1138 XSetScreenSaver( p_vout->p_sys->p_display, 0,
1139 p_vout->p_sys->i_ss_interval,
1140 p_vout->p_sys->i_ss_blanking,
1141 p_vout->p_sys->i_ss_exposure );
1144 if( DPMSQueryExtension( p_vout->p_sys->p_display, &dummy, &dummy ) )
1147 /* Save DPMS current state */
1148 DPMSInfo( p_vout->p_sys->p_display, &dummy,
1149 &p_vout->p_sys->b_ss_dpms );
1150 DPMSDisable( p_vout->p_sys->p_display );
1154 /*****************************************************************************
1155 * CreateCursor: create a blank mouse pointer
1156 *****************************************************************************/
1157 static void CreateCursor( vout_thread_t *p_vout )
1159 XColor cursor_color;
1161 p_vout->p_sys->cursor_pixmap =
1162 XCreatePixmap( p_vout->p_sys->p_display,
1163 DefaultRootWindow( p_vout->p_sys->p_display ),
1166 XParseColor( p_vout->p_sys->p_display,
1167 XCreateColormap( p_vout->p_sys->p_display,
1169 p_vout->p_sys->p_display ),
1171 p_vout->p_sys->p_display,
1172 p_vout->p_sys->i_screen ),
1174 "black", &cursor_color );
1176 p_vout->p_sys->blank_cursor =
1177 XCreatePixmapCursor( p_vout->p_sys->p_display,
1178 p_vout->p_sys->cursor_pixmap,
1179 p_vout->p_sys->cursor_pixmap,
1180 &cursor_color, &cursor_color, 1, 1 );
1183 /*****************************************************************************
1184 * DestroyCursor: destroy the blank mouse pointer
1185 *****************************************************************************/
1186 static void DestroyCursor( vout_thread_t *p_vout )
1188 XFreePixmap( p_vout->p_sys->p_display, p_vout->p_sys->cursor_pixmap );
1191 /*****************************************************************************
1192 * ToggleCursor: hide or show the mouse pointer
1193 *****************************************************************************
1194 * This function hides the X pointer if it is visible by setting the pointer
1195 * sprite to a blank one. To show it again, we disable the sprite.
1196 *****************************************************************************/
1197 static void ToggleCursor( vout_thread_t *p_vout )
1199 if( p_vout->p_sys->b_mouse_pointer_visible )
1201 XDefineCursor( p_vout->p_sys->p_display,
1202 p_vout->p_sys->window,
1203 p_vout->p_sys->blank_cursor );
1204 p_vout->p_sys->b_mouse_pointer_visible = 0;
1208 XUndefineCursor( p_vout->p_sys->p_display, p_vout->p_sys->window );
1209 p_vout->p_sys->b_mouse_pointer_visible = 1;