]> git.sesse.net Git - vlc/blob - modules/video_output/x11/xcommon.c
0dd87b4682406c126fa37fb407dc6eaff2c56d7f
[vlc] / modules / video_output / x11 / xcommon.c
1 /*****************************************************************************
2  * xcommon.c: Functions common to the X11 and XVideo plugins
3  *****************************************************************************
4  * Copyright (C) 1998-2006 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Vincent Seguin <seguin@via.ecp.fr>
8  *          Sam Hocevar <sam@zoy.org>
9  *          David Kennedy <dkennedy@tinytoad.com>
10  *          Gildas Bazin <gbazin@videolan.org>
11  *
12  * This program is free software; you can redistribute it and/or modify
13  * it under the terms of the GNU General Public License as published by
14  * the Free Software Foundation; either version 2 of the License, or
15  * (at your option) any later version.
16  *
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with this program; if not, write to the Free Software
24  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
25  *****************************************************************************/
26
27 /*****************************************************************************
28  * Preamble
29  *****************************************************************************/
30 #ifdef HAVE_CONFIG_H
31 # include "config.h"
32 #endif
33
34 #include <vlc_common.h>
35 #include <vlc_playlist.h>
36 #include <vlc_vout.h>
37 #include <vlc_vout_window.h>
38 #include <vlc_keys.h>
39
40 #ifdef HAVE_MACHINE_PARAM_H
41     /* BSD */
42 #   include <machine/param.h>
43 #   include <sys/types.h>                                  /* typedef ushort */
44 #   include <sys/ipc.h>
45 #endif
46
47 #include <X11/Xlib.h>
48 #include <X11/Xproto.h>
49 #include <X11/Xmd.h>
50 #include <X11/Xutil.h>
51 #ifdef DPMSINFO_IN_DPMS_H
52 #   include <X11/extensions/dpms.h>
53 #endif
54
55 #ifdef MODULE_NAME_IS_glx
56 #   include <GL/glx.h>
57 #endif
58
59 #include "xcommon.h"
60
61 /*****************************************************************************
62  * Local prototypes
63  *****************************************************************************/
64 int  Activate   ( vlc_object_t * );
65 void Deactivate ( vlc_object_t * );
66
67 static int  ManageVideo    ( vout_thread_t * );
68 static int  Control        ( vout_thread_t *, int, va_list );
69
70 static int  CreateWindow   ( vout_thread_t *, x11_window_t * );
71 static void DestroyWindow  ( vout_thread_t *, x11_window_t * );
72
73 static void ToggleFullScreen      ( vout_thread_t * );
74
75 static void EnableXScreenSaver    ( vout_thread_t * );
76 static void DisableXScreenSaver   ( vout_thread_t * );
77
78 static void CreateCursor   ( vout_thread_t * );
79 static void DestroyCursor  ( vout_thread_t * );
80 static void ToggleCursor   ( vout_thread_t * );
81
82 static int X11ErrorHandler( Display *, XErrorEvent * );
83
84 /*****************************************************************************
85  * Activate: allocate X11 video thread output method
86  *****************************************************************************
87  * This function allocate and initialize a X11 vout method. It uses some of the
88  * vout properties to choose the window size, and change them according to the
89  * actual properties of the display.
90  *****************************************************************************/
91 int Activate ( vlc_object_t *p_this )
92 {
93     vout_thread_t *p_vout = (vout_thread_t *)p_this;
94     char *        psz_display;
95
96     p_vout->pf_render = NULL;
97     p_vout->pf_manage = ManageVideo;
98     p_vout->pf_control = Control;
99
100     /* Allocate structure */
101     p_vout->p_sys = malloc( sizeof( vout_sys_t ) );
102     if( p_vout->p_sys == NULL )
103         return VLC_ENOMEM;
104
105     /* Open display, using the "display" config variable or the DISPLAY
106      * environment variable */
107     psz_display = config_GetPsz( p_vout, MODULE_STRING "-display" );
108
109     p_vout->p_sys->p_display = XOpenDisplay( psz_display );
110
111     if( p_vout->p_sys->p_display == NULL )                         /* error */
112     {
113         msg_Err( p_vout, "cannot open display %s",
114                          XDisplayName( psz_display ) );
115         free( p_vout->p_sys );
116         free( psz_display );
117         return VLC_EGENERIC;
118     }
119     free( psz_display );
120
121     /* Replace error handler so we can intercept some non-fatal errors */
122     XSetErrorHandler( X11ErrorHandler );
123
124     /* Get a screen ID matching the XOpenDisplay return value */
125     p_vout->p_sys->i_screen = DefaultScreen( p_vout->p_sys->p_display );
126
127 #if defined(MODULE_NAME_IS_glx)
128     {
129         int i_opcode, i_evt, i_err = 0;
130         int i_maj, i_min = 0;
131
132         /* Check for GLX extension */
133         if( !XQueryExtension( p_vout->p_sys->p_display, "GLX",
134                               &i_opcode, &i_evt, &i_err ) )
135         {
136             msg_Err( p_this, "GLX extension not supported" );
137             XCloseDisplay( p_vout->p_sys->p_display );
138             free( p_vout->p_sys );
139             return VLC_EGENERIC;
140         }
141         if( !glXQueryExtension( p_vout->p_sys->p_display, &i_err, &i_evt ) )
142         {
143             msg_Err( p_this, "glXQueryExtension failed" );
144             XCloseDisplay( p_vout->p_sys->p_display );
145             free( p_vout->p_sys );
146             return VLC_EGENERIC;
147         }
148
149         /* Check GLX version */
150         if (!glXQueryVersion( p_vout->p_sys->p_display, &i_maj, &i_min ) )
151         {
152             msg_Err( p_this, "glXQueryVersion failed" );
153             XCloseDisplay( p_vout->p_sys->p_display );
154             free( p_vout->p_sys );
155             return VLC_EGENERIC;
156         }
157         if( i_maj <= 0 || ((i_maj == 1) && (i_min < 3)) )
158         {
159             p_vout->p_sys->b_glx13 = false;
160             msg_Dbg( p_this, "using GLX 1.2 API" );
161         }
162         else
163         {
164             p_vout->p_sys->b_glx13 = true;
165             msg_Dbg( p_this, "using GLX 1.3 API" );
166         }
167     }
168 #endif
169
170     /* Create blank cursor (for mouse cursor autohiding) */
171     p_vout->p_sys->i_time_mouse_last_moved = mdate();
172     p_vout->p_sys->i_mouse_hide_timeout =
173         var_GetInteger(p_vout, "mouse-hide-timeout") * 1000;
174     p_vout->p_sys->b_mouse_pointer_visible = 1;
175     CreateCursor( p_vout );
176
177     /* Set main window's size */
178     p_vout->p_sys->window.i_x      = 0;
179     p_vout->p_sys->window.i_y      = 0;
180     p_vout->p_sys->window.i_width  = p_vout->i_window_width;
181     p_vout->p_sys->window.i_height = p_vout->i_window_height;
182     var_Create( p_vout, "video-title", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
183     /* Spawn base window - this window will include the video output window,
184      * but also command buttons, subtitles and other indicators */
185     if( CreateWindow( p_vout, &p_vout->p_sys->window ) )
186     {
187         msg_Err( p_vout, "cannot create X11 window" );
188         DestroyCursor( p_vout );
189         XCloseDisplay( p_vout->p_sys->p_display );
190         free( p_vout->p_sys );
191         return VLC_EGENERIC;
192     }
193
194     /* Disable screen saver */
195     DisableXScreenSaver( p_vout );
196
197     /* Misc init */
198     p_vout->p_sys->i_time_button_last_pressed = 0;
199
200     /* Variable to indicate if the window should be on top of others */
201     /* Trigger a callback right now */
202     var_TriggerCallback( p_vout, "video-on-top" );
203
204     return VLC_SUCCESS;
205 }
206
207 /*****************************************************************************
208  * Deactivate: destroy X11 video thread output method
209  *****************************************************************************
210  * Terminate an output method created by Open
211  *****************************************************************************/
212 void Deactivate ( vlc_object_t *p_this )
213 {
214     vout_thread_t *p_vout = (vout_thread_t *)p_this;
215
216     /* Restore cursor if it was blanked */
217     if( !p_vout->p_sys->b_mouse_pointer_visible )
218     {
219         ToggleCursor( p_vout );
220     }
221
222     DestroyCursor( p_vout );
223     EnableXScreenSaver( p_vout );
224     DestroyWindow( p_vout, &p_vout->p_sys->window );
225     XCloseDisplay( p_vout->p_sys->p_display );
226
227     /* Destroy structure */
228     free( p_vout->p_sys );
229 }
230
231 /*****************************************************************************
232  * ManageVideo: handle X11 events
233  *****************************************************************************
234  * This function should be called regularly by video output thread. It manages
235  * X11 events and allows window resizing. It returns a non null value on
236  * error.
237  *****************************************************************************/
238 static int ManageVideo( vout_thread_t *p_vout )
239 {
240     XEvent      xevent;                                         /* X11 event */
241     vlc_value_t val;
242
243     /* Handle events from the owner window */
244     while( XCheckWindowEvent( p_vout->p_sys->p_display,
245                               p_vout->p_sys->window.owner_window->handle.xid,
246                               StructureNotifyMask, &xevent ) == True )
247     {
248         /* ConfigureNotify event: prepare  */
249         if( xevent.type == ConfigureNotify )
250             /* Update dimensions */
251             XResizeWindow( p_vout->p_sys->p_display,
252                            p_vout->p_sys->window.base_window,
253                            xevent.xconfigure.width,
254                            xevent.xconfigure.height );
255     }
256
257     /* Handle X11 events: ConfigureNotify events are parsed to know if the
258      * output window's size changed, MapNotify and UnmapNotify to know if the
259      * window is mapped (and if the display is useful), and ClientMessages
260      * to intercept window destruction requests */
261
262     while( XCheckWindowEvent( p_vout->p_sys->p_display,
263                               p_vout->p_sys->window.base_window,
264                               StructureNotifyMask |
265                               ButtonPressMask | ButtonReleaseMask |
266                               PointerMotionMask | Button1MotionMask , &xevent )
267            == True )
268     {
269         /* ConfigureNotify event: prepare  */
270         if( xevent.type == ConfigureNotify )
271         {
272             if( (unsigned int)xevent.xconfigure.width
273                    != p_vout->p_sys->window.i_width
274               || (unsigned int)xevent.xconfigure.height
275                     != p_vout->p_sys->window.i_height )
276             {
277                 /* Update dimensions */
278                 p_vout->i_changes |= VOUT_SIZE_CHANGE;
279                 p_vout->p_sys->window.i_width = xevent.xconfigure.width;
280                 p_vout->p_sys->window.i_height = xevent.xconfigure.height;
281             }
282         }
283         /* Mouse click */
284         else if( xevent.type == ButtonPress )
285         {
286             switch( ((XButtonEvent *)&xevent)->button )
287             {
288                 case Button1:
289                     var_Get( p_vout, "mouse-button-down", &val );
290                     val.i_int |= 1;
291                     var_Set( p_vout, "mouse-button-down", val );
292
293                     var_SetBool( p_vout->p_libvlc, "intf-popupmenu", false );
294
295                     /* detect double-clicks */
296                     if( ( ((XButtonEvent *)&xevent)->time -
297                           p_vout->p_sys->i_time_button_last_pressed ) < 300 )
298                     {
299                         p_vout->i_changes |= VOUT_FULLSCREEN_CHANGE;
300                     }
301
302                     p_vout->p_sys->i_time_button_last_pressed =
303                         ((XButtonEvent *)&xevent)->time;
304                     break;
305                 case Button2:
306                     var_Get( p_vout, "mouse-button-down", &val );
307                     val.i_int |= 2;
308                     var_Set( p_vout, "mouse-button-down", val );
309                     break;
310
311                 case Button3:
312                     var_Get( p_vout, "mouse-button-down", &val );
313                     val.i_int |= 4;
314                     var_Set( p_vout, "mouse-button-down", val );
315                     var_SetBool( p_vout->p_libvlc, "intf-popupmenu", true );
316                     break;
317
318                 case Button4:
319                     var_Get( p_vout, "mouse-button-down", &val );
320                     val.i_int |= 8;
321                     var_Set( p_vout, "mouse-button-down", val );
322                     break;
323
324                 case Button5:
325                     var_Get( p_vout, "mouse-button-down", &val );
326                     val.i_int |= 16;
327                     var_Set( p_vout, "mouse-button-down", val );
328                     break;
329             }
330         }
331         /* Mouse release */
332         else if( xevent.type == ButtonRelease )
333         {
334             switch( ((XButtonEvent *)&xevent)->button )
335             {
336                 case Button1:
337                     {
338                         var_Get( p_vout, "mouse-button-down", &val );
339                         val.i_int &= ~1;
340                         var_Set( p_vout, "mouse-button-down", val );
341
342                         var_SetBool( p_vout, "mouse-clicked", true );
343                     }
344                     break;
345
346                 case Button2:
347                     {
348                         var_Get( p_vout, "mouse-button-down", &val );
349                         val.i_int &= ~2;
350                         var_Set( p_vout, "mouse-button-down", val );
351
352                         var_ToggleBool( p_vout->p_libvlc, "intf-show" );
353                     }
354                     break;
355
356                 case Button3:
357                     {
358                         var_Get( p_vout, "mouse-button-down", &val );
359                         val.i_int &= ~4;
360                         var_Set( p_vout, "mouse-button-down", val );
361
362                     }
363                     break;
364
365                 case Button4:
366                     var_Get( p_vout, "mouse-button-down", &val );
367                     val.i_int &= ~8;
368                     var_Set( p_vout, "mouse-button-down", val );
369                     break;
370
371                 case Button5:
372                     var_Get( p_vout, "mouse-button-down", &val );
373                     val.i_int &= ~16;
374                     var_Set( p_vout, "mouse-button-down", val );
375                     break;
376
377             }
378         }
379         /* Mouse move */
380         else if( xevent.type == MotionNotify )
381         {
382             unsigned int i_width, i_height, i_x, i_y;
383
384             /* somewhat different use for vout_PlacePicture:
385              * here the values are needed to give to mouse coordinates
386              * in the original picture space */
387             vout_PlacePicture( p_vout, p_vout->p_sys->window.i_width,
388                                p_vout->p_sys->window.i_height,
389                                &i_x, &i_y, &i_width, &i_height );
390
391             /* Compute the x coordinate and check if the value is
392                in [0,p_vout->fmt_in.i_visible_width] */
393             val.i_int = ( xevent.xmotion.x - i_x ) *
394                 p_vout->fmt_in.i_visible_width / i_width +
395                 p_vout->fmt_in.i_x_offset;
396
397             if( (int)(xevent.xmotion.x - i_x) < 0 )
398                 val.i_int = 0;
399             else if( (unsigned int)val.i_int > p_vout->fmt_in.i_visible_width )
400                 val.i_int = p_vout->fmt_in.i_visible_width;
401
402             var_Set( p_vout, "mouse-x", val );
403
404             /* compute the y coordinate and check if the value is
405                in [0,p_vout->fmt_in.i_visible_height] */
406             val.i_int = ( xevent.xmotion.y - i_y ) *
407                 p_vout->fmt_in.i_visible_height / i_height +
408                 p_vout->fmt_in.i_y_offset;
409
410             if( (int)(xevent.xmotion.y - i_y) < 0 )
411                 val.i_int = 0;
412             else if( (unsigned int)val.i_int > p_vout->fmt_in.i_visible_height )
413                 val.i_int = p_vout->fmt_in.i_visible_height;
414
415             var_Set( p_vout, "mouse-y", val );
416
417             var_SetBool( p_vout, "mouse-moved", true );
418
419             p_vout->p_sys->i_time_mouse_last_moved = mdate();
420             if( ! p_vout->p_sys->b_mouse_pointer_visible )
421             {
422                 ToggleCursor( p_vout );
423             }
424         }
425         else if( xevent.type == ReparentNotify /* XXX: why do we get this? */
426                   || xevent.type == MapNotify
427                   || xevent.type == UnmapNotify )
428         {
429             /* Ignore these events */
430         }
431         else /* Other events */
432         {
433             msg_Warn( p_vout, "unhandled event %d received", xevent.type );
434         }
435     }
436
437     /* Handle events for video output sub-window */
438     while( XCheckWindowEvent( p_vout->p_sys->p_display,
439                               p_vout->p_sys->window.video_window,
440                               ExposureMask, &xevent ) == True );
441
442     /* ClientMessage event - only WM_PROTOCOLS with WM_DELETE_WINDOW data
443      * are handled - according to the man pages, the format is always 32
444      * in this case */
445     while( XCheckTypedEvent( p_vout->p_sys->p_display,
446                              ClientMessage, &xevent ) )
447     {
448         if( (xevent.xclient.message_type == p_vout->p_sys->window.wm_protocols)
449                && ((Atom)xevent.xclient.data.l[0]
450                      == p_vout->p_sys->window.wm_delete_window ) )
451         {
452             /* the user wants to close the window */
453             playlist_t * p_playlist = pl_Hold( p_vout );
454             if( p_playlist != NULL )
455             {
456                 playlist_Stop( p_playlist );
457                 pl_Release( p_vout );
458             }
459         }
460     }
461
462     /*
463      * Fullscreen Change
464      */
465     if ( p_vout->i_changes & VOUT_FULLSCREEN_CHANGE )
466     {
467         /* Update the object variable and trigger callback */
468         var_SetBool( p_vout, "fullscreen", !p_vout->b_fullscreen );
469
470         ToggleFullScreen( p_vout );
471         p_vout->i_changes &= ~VOUT_FULLSCREEN_CHANGE;
472     }
473
474     /* autoscale toggle */
475     if( p_vout->i_changes & VOUT_SCALE_CHANGE )
476     {
477         p_vout->i_changes &= ~VOUT_SCALE_CHANGE;
478
479         p_vout->b_autoscale = var_GetBool( p_vout, "autoscale" );
480         p_vout->i_zoom = ZOOM_FP_FACTOR;
481
482         p_vout->i_changes |= VOUT_SIZE_CHANGE;
483     }
484
485     /* scaling factor */
486     if( p_vout->i_changes & VOUT_ZOOM_CHANGE )
487     {
488         p_vout->i_changes &= ~VOUT_ZOOM_CHANGE;
489
490         p_vout->b_autoscale = false;
491         p_vout->i_zoom =
492             (int)( ZOOM_FP_FACTOR * var_GetFloat( p_vout, "scale" ) );
493
494         p_vout->i_changes |= VOUT_SIZE_CHANGE;
495     }
496
497     if( p_vout->i_changes & VOUT_CROP_CHANGE ||
498         p_vout->i_changes & VOUT_ASPECT_CHANGE )
499     {
500         p_vout->i_changes &= ~VOUT_CROP_CHANGE;
501         p_vout->i_changes &= ~VOUT_ASPECT_CHANGE;
502
503         p_vout->fmt_out.i_x_offset = p_vout->fmt_in.i_x_offset;
504         p_vout->fmt_out.i_y_offset = p_vout->fmt_in.i_y_offset;
505         p_vout->fmt_out.i_visible_width = p_vout->fmt_in.i_visible_width;
506         p_vout->fmt_out.i_visible_height = p_vout->fmt_in.i_visible_height;
507         p_vout->fmt_out.i_aspect = p_vout->fmt_in.i_aspect;
508         p_vout->fmt_out.i_sar_num = p_vout->fmt_in.i_sar_num;
509         p_vout->fmt_out.i_sar_den = p_vout->fmt_in.i_sar_den;
510         p_vout->output.i_aspect = p_vout->fmt_in.i_aspect;
511
512         p_vout->i_changes |= VOUT_SIZE_CHANGE;
513     }
514
515     /*
516      * Size change
517      *
518      * (Needs to be placed after VOUT_FULLSREEN_CHANGE because we can activate
519      *  the size flag inside the fullscreen routine)
520      */
521     if( p_vout->i_changes & VOUT_SIZE_CHANGE )
522     {
523         unsigned int i_width, i_height, i_x, i_y;
524
525         p_vout->i_changes &= ~VOUT_SIZE_CHANGE;
526
527         vout_PlacePicture( p_vout, p_vout->p_sys->window.i_width,
528                            p_vout->p_sys->window.i_height,
529                            &i_x, &i_y, &i_width, &i_height );
530
531         XMoveResizeWindow( p_vout->p_sys->p_display,
532                            p_vout->p_sys->window.video_window,
533                            i_x, i_y, i_width, i_height );
534     }
535
536     /* Autohide Cursour */
537     if( mdate() - p_vout->p_sys->i_time_mouse_last_moved >
538         p_vout->p_sys->i_mouse_hide_timeout )
539     {
540         /* Hide the mouse automatically */
541         if( p_vout->p_sys->b_mouse_pointer_visible )
542         {
543             ToggleCursor( p_vout );
544         }
545     }
546
547     return 0;
548 }
549
550 /* following functions are local */
551
552 /*****************************************************************************
553  * CreateWindow: open and set-up X11 main window
554  *****************************************************************************/
555 static int CreateWindow( vout_thread_t *p_vout, x11_window_t *p_win )
556 {
557     XSizeHints              xsize_hints;
558     XSetWindowAttributes    xwindow_attributes;
559     XGCValues               xgcvalues;
560     XEvent                  xevent;
561
562     bool              b_map_notify = false;
563
564     /* Prepare window manager hints and properties */
565     p_win->wm_protocols =
566              XInternAtom( p_vout->p_sys->p_display, "WM_PROTOCOLS", True );
567     p_win->wm_delete_window =
568              XInternAtom( p_vout->p_sys->p_display, "WM_DELETE_WINDOW", True );
569
570     /* Never have a 0-pixel-wide window */
571     xsize_hints.min_width = 2;
572     xsize_hints.min_height = 1;
573
574     /* Prepare window attributes */
575     xwindow_attributes.backing_store = Always;       /* save the hidden part */
576     xwindow_attributes.background_pixel = BlackPixel(p_vout->p_sys->p_display,
577                                                      p_vout->p_sys->i_screen);
578     xwindow_attributes.event_mask = ExposureMask | StructureNotifyMask;
579
580     {
581         vout_window_cfg_t wnd_cfg;
582         memset( &wnd_cfg, 0, sizeof(wnd_cfg) );
583         wnd_cfg.type   = VOUT_WINDOW_TYPE_XID;
584         wnd_cfg.x      = p_win->i_x;
585         wnd_cfg.y      = p_win->i_y;
586         wnd_cfg.width  = p_win->i_width;
587         wnd_cfg.height = p_win->i_height;
588
589         p_win->owner_window = vout_window_New( VLC_OBJECT(p_vout), NULL, &wnd_cfg );
590         if( !p_win->owner_window )
591             return VLC_EGENERIC;
592         xsize_hints.base_width  = xsize_hints.width = p_win->i_width;
593         xsize_hints.base_height = xsize_hints.height = p_win->i_height;
594         xsize_hints.flags       = PSize | PMinSize;
595
596         if( p_win->i_x >=0 || p_win->i_y >= 0 )
597         {
598             xsize_hints.x = p_win->i_x;
599             xsize_hints.y = p_win->i_y;
600             xsize_hints.flags |= PPosition;
601         }
602
603         /* Select events we are interested in. */
604         XSelectInput( p_vout->p_sys->p_display,
605                       p_win->owner_window->handle.xid, StructureNotifyMask );
606
607         /* Get the parent window's geometry information */
608         XGetGeometry( p_vout->p_sys->p_display,
609                       p_win->owner_window->handle.xid,
610                       &(Window){ 0 }, &(int){ 0 }, &(int){ 0 },
611                       &p_win->i_width,
612                       &p_win->i_height,
613                       &(unsigned){ 0 }, &(unsigned){ 0 } );
614
615         /* From man XSelectInput: only one client at a time can select a
616          * ButtonPress event, so we need to open a new window anyway. */
617         p_win->base_window =
618             XCreateWindow( p_vout->p_sys->p_display,
619                            p_win->owner_window->handle.xid,
620                            0, 0,
621                            p_win->i_width, p_win->i_height,
622                            0,
623                            0, CopyFromParent, 0,
624                            CWBackingStore | CWBackPixel | CWEventMask,
625                            &xwindow_attributes );
626     }
627
628     if( (p_win->wm_protocols == None)        /* use WM_DELETE_WINDOW */
629         || (p_win->wm_delete_window == None)
630         || !XSetWMProtocols( p_vout->p_sys->p_display, p_win->base_window,
631                              &p_win->wm_delete_window, 1 ) )
632     {
633         /* WM_DELETE_WINDOW is not supported by window manager */
634         msg_Warn( p_vout, "missing or bad window manager" );
635     }
636
637     /* Creation of a graphic context that doesn't generate a GraphicsExpose
638      * event when using functions like XCopyArea */
639     xgcvalues.graphics_exposures = False;
640     p_win->gc = XCreateGC( p_vout->p_sys->p_display,
641                            p_win->base_window,
642                            GCGraphicsExposures, &xgcvalues );
643
644     /* Wait till the window is mapped */
645     XMapWindow( p_vout->p_sys->p_display, p_win->base_window );
646     do
647     {
648         XWindowEvent( p_vout->p_sys->p_display, p_win->base_window,
649                       SubstructureNotifyMask | StructureNotifyMask, &xevent);
650         if( (xevent.type == MapNotify)
651                  && (xevent.xmap.window == p_win->base_window) )
652         {
653             b_map_notify = true;
654         }
655         else if( (xevent.type == ConfigureNotify)
656                  && (xevent.xconfigure.window == p_win->base_window) )
657         {
658             p_win->i_width = xevent.xconfigure.width;
659             p_win->i_height = xevent.xconfigure.height;
660         }
661     } while( !b_map_notify );
662
663     long mask = StructureNotifyMask | PointerMotionMask;
664     if( var_CreateGetBool( p_vout, "mouse-events" ) )
665         mask |= ButtonPressMask | ButtonReleaseMask;
666     XSelectInput( p_vout->p_sys->p_display, p_win->base_window, mask );
667
668     /* Create video output sub-window. */
669     p_win->video_window =  XCreateSimpleWindow(
670                                       p_vout->p_sys->p_display,
671                                       p_win->base_window, 0, 0,
672                                       p_win->i_width, p_win->i_height,
673                                       0,
674                                       BlackPixel( p_vout->p_sys->p_display,
675                                                   p_vout->p_sys->i_screen ),
676                                       WhitePixel( p_vout->p_sys->p_display,
677                                                   p_vout->p_sys->i_screen ) );
678
679     XSetWindowBackground( p_vout->p_sys->p_display, p_win->video_window,
680                           BlackPixel( p_vout->p_sys->p_display,
681                                       p_vout->p_sys->i_screen ) );
682
683     XMapWindow( p_vout->p_sys->p_display, p_win->video_window );
684     XSelectInput( p_vout->p_sys->p_display, p_win->video_window,
685                   ExposureMask );
686
687     /* make sure the video window will be centered in the next ManageVideo() */
688     p_vout->i_changes |= VOUT_SIZE_CHANGE;
689
690     /* If the cursor was formerly blank than blank it again */
691     if( !p_vout->p_sys->b_mouse_pointer_visible )
692     {
693         ToggleCursor( p_vout );
694         ToggleCursor( p_vout );
695     }
696
697     /* Do NOT use XFlush here ! */
698     XSync( p_vout->p_sys->p_display, False );
699
700     return VLC_SUCCESS;
701 }
702
703 /*****************************************************************************
704  * DestroyWindow: destroy the window
705  *****************************************************************************
706  *
707  *****************************************************************************/
708 static void DestroyWindow( vout_thread_t *p_vout, x11_window_t *p_win )
709 {
710     /* Do NOT use XFlush here ! */
711     XSync( p_vout->p_sys->p_display, False );
712
713     if( p_win->video_window != None )
714         XDestroyWindow( p_vout->p_sys->p_display, p_win->video_window );
715
716     XFreeGC( p_vout->p_sys->p_display, p_win->gc );
717
718     XUnmapWindow( p_vout->p_sys->p_display, p_win->base_window );
719     XDestroyWindow( p_vout->p_sys->p_display, p_win->base_window );
720
721     /* make sure base window is destroyed before proceeding further */
722     bool b_destroy_notify = false;
723     do
724     {
725         XEvent      xevent;
726         XWindowEvent( p_vout->p_sys->p_display, p_win->base_window,
727                       SubstructureNotifyMask | StructureNotifyMask, &xevent);
728         if( (xevent.type == DestroyNotify)
729                  && (xevent.xmap.window == p_win->base_window) )
730         {
731             b_destroy_notify = true;
732         }
733     } while( !b_destroy_notify );
734
735     vout_window_Delete( p_win->owner_window );
736 }
737
738 /*****************************************************************************
739  * ToggleFullScreen: Enable or disable full screen mode
740  *****************************************************************************
741  * This function will switch between fullscreen and window mode.
742  *****************************************************************************/
743 static void ToggleFullScreen ( vout_thread_t *p_vout )
744 {
745     p_vout->b_fullscreen = !p_vout->b_fullscreen;
746     vout_window_SetFullScreen( p_vout->p_sys->window.owner_window,
747                                p_vout->b_fullscreen );
748 }
749
750 /*****************************************************************************
751  * EnableXScreenSaver: enable screen saver
752  *****************************************************************************
753  * This function enables the screen saver on a display after it has been
754  * disabled by XDisableScreenSaver.
755  * FIXME: what happens if multiple vlc sessions are running at the same
756  *        time ???
757  *****************************************************************************/
758 static void EnableXScreenSaver( vout_thread_t *p_vout )
759 {
760 #ifdef DPMSINFO_IN_DPMS_H
761     int dummy;
762 #endif
763
764     if( p_vout->p_sys->i_ss_timeout )
765     {
766         XSetScreenSaver( p_vout->p_sys->p_display, p_vout->p_sys->i_ss_timeout,
767                          p_vout->p_sys->i_ss_interval,
768                          p_vout->p_sys->i_ss_blanking,
769                          p_vout->p_sys->i_ss_exposure );
770     }
771
772     /* Restore DPMS settings */
773 #ifdef DPMSINFO_IN_DPMS_H
774     if( DPMSQueryExtension( p_vout->p_sys->p_display, &dummy, &dummy ) )
775     {
776         if( p_vout->p_sys->b_ss_dpms )
777         {
778             DPMSEnable( p_vout->p_sys->p_display );
779         }
780     }
781 #endif
782 }
783
784 /*****************************************************************************
785  * DisableXScreenSaver: disable screen saver
786  *****************************************************************************
787  * See XEnableXScreenSaver
788  *****************************************************************************/
789 static void DisableXScreenSaver( vout_thread_t *p_vout )
790 {
791 #ifdef DPMSINFO_IN_DPMS_H
792     int dummy;
793 #endif
794
795     /* Save screen saver information */
796     XGetScreenSaver( p_vout->p_sys->p_display, &p_vout->p_sys->i_ss_timeout,
797                      &p_vout->p_sys->i_ss_interval,
798                      &p_vout->p_sys->i_ss_blanking,
799                      &p_vout->p_sys->i_ss_exposure );
800
801     /* Disable screen saver */
802     if( p_vout->p_sys->i_ss_timeout )
803     {
804         XSetScreenSaver( p_vout->p_sys->p_display, 0,
805                          p_vout->p_sys->i_ss_interval,
806                          p_vout->p_sys->i_ss_blanking,
807                          p_vout->p_sys->i_ss_exposure );
808     }
809
810     /* Disable DPMS */
811 #ifdef DPMSINFO_IN_DPMS_H
812     if( DPMSQueryExtension( p_vout->p_sys->p_display, &dummy, &dummy ) )
813     {
814         CARD16 unused;
815         /* Save DPMS current state */
816         DPMSInfo( p_vout->p_sys->p_display, &unused,
817                   &p_vout->p_sys->b_ss_dpms );
818         DPMSDisable( p_vout->p_sys->p_display );
819    }
820 #endif
821 }
822
823 /*****************************************************************************
824  * CreateCursor: create a blank mouse pointer
825  *****************************************************************************/
826 static void CreateCursor( vout_thread_t *p_vout )
827 {
828     XColor cursor_color;
829
830     p_vout->p_sys->cursor_pixmap =
831         XCreatePixmap( p_vout->p_sys->p_display,
832                        DefaultRootWindow( p_vout->p_sys->p_display ),
833                        1, 1, 1 );
834
835     XParseColor( p_vout->p_sys->p_display,
836                  XCreateColormap( p_vout->p_sys->p_display,
837                                   DefaultRootWindow(
838                                                     p_vout->p_sys->p_display ),
839                                   DefaultVisual(
840                                                 p_vout->p_sys->p_display,
841                                                 p_vout->p_sys->i_screen ),
842                                   AllocNone ),
843                  "black", &cursor_color );
844
845     p_vout->p_sys->blank_cursor =
846         XCreatePixmapCursor( p_vout->p_sys->p_display,
847                              p_vout->p_sys->cursor_pixmap,
848                              p_vout->p_sys->cursor_pixmap,
849                              &cursor_color, &cursor_color, 1, 1 );
850 }
851
852 /*****************************************************************************
853  * DestroyCursor: destroy the blank mouse pointer
854  *****************************************************************************/
855 static void DestroyCursor( vout_thread_t *p_vout )
856 {
857     XFreePixmap( p_vout->p_sys->p_display, p_vout->p_sys->cursor_pixmap );
858 }
859
860 /*****************************************************************************
861  * ToggleCursor: hide or show the mouse pointer
862  *****************************************************************************
863  * This function hides the X pointer if it is visible by setting the pointer
864  * sprite to a blank one. To show it again, we disable the sprite.
865  *****************************************************************************/
866 static void ToggleCursor( vout_thread_t *p_vout )
867 {
868     if( p_vout->p_sys->b_mouse_pointer_visible )
869     {
870         XDefineCursor( p_vout->p_sys->p_display,
871                        p_vout->p_sys->window.base_window,
872                        p_vout->p_sys->blank_cursor );
873         p_vout->p_sys->b_mouse_pointer_visible = 0;
874     }
875     else
876     {
877         XUndefineCursor( p_vout->p_sys->p_display,
878                          p_vout->p_sys->window.base_window );
879         p_vout->p_sys->b_mouse_pointer_visible = 1;
880     }
881 }
882
883 /*****************************************************************************
884  * X11ErrorHandler: replace error handler so we can intercept some of them
885  *****************************************************************************/
886 static int X11ErrorHandler( Display * display, XErrorEvent * event )
887 {
888     char txt[1024];
889
890     XGetErrorText( display, event->error_code, txt, sizeof( txt ) );
891     fprintf( stderr,
892              "[????????] x11 video output error: X11 request %u.%u failed "
893               "with error code %u:\n %s\n",
894              event->request_code, event->minor_code, event->error_code, txt );
895
896     switch( event->request_code )
897     {
898     case X_SetInputFocus:
899         /* Ignore errors on XSetInputFocus()
900          * (they happen when a window is not yet mapped) */
901         return 0;
902     }
903
904     XSetErrorHandler(NULL);
905     return (XSetErrorHandler(X11ErrorHandler))( display, event );
906 }
907
908 /*****************************************************************************
909  * Control: control facility for the vout
910  *****************************************************************************/
911 static int Control( vout_thread_t *p_vout, int i_query, va_list args )
912 {
913     unsigned int i_width, i_height;
914
915     switch( i_query )
916     {
917         case VOUT_SET_SIZE:
918             i_width  = va_arg( args, unsigned int );
919             i_height = va_arg( args, unsigned int );
920             if( !i_width ) i_width = p_vout->i_window_width;
921             if( !i_height ) i_height = p_vout->i_window_height;
922
923             return vout_window_SetSize( p_vout->p_sys->window.owner_window,
924                                         i_width, i_height);
925
926         case VOUT_SET_STAY_ON_TOP:
927             return vout_window_SetOnTop( p_vout->p_sys->window.owner_window,
928                                          va_arg( args, int ) );
929
930        default:
931             return VLC_EGENERIC;
932     }
933 }