]> git.sesse.net Git - vlc/blob - modules/video_output/x11/xcommon.c
* 2nd review of video* and control/telnet.c
[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 #include <errno.h>                                                 /* ENOMEM */
31 #include <stdlib.h>                                                /* free() */
32 #include <string.h>                                            /* strerror() */
33
34 #include <vlc/vlc.h>
35 #include <vlc/intf.h>
36 #include <vlc/vout.h>
37 #include <vlc_keys.h>
38
39 #ifdef HAVE_MACHINE_PARAM_H
40     /* BSD */
41 #   include <machine/param.h>
42 #   include <sys/types.h>                                  /* typedef ushort */
43 #   include <sys/ipc.h>
44 #endif
45
46 #ifndef WIN32
47 #   include <netinet/in.h>                            /* BSD: struct in_addr */
48 #endif
49
50 #ifdef HAVE_SYS_SHM_H
51 #   include <sys/shm.h>                                /* shmget(), shmctl() */
52 #endif
53
54 #include <X11/Xlib.h>
55 #include <X11/Xproto.h>
56 #include <X11/Xmd.h>
57 #include <X11/Xutil.h>
58 #include <X11/keysym.h>
59 #ifdef HAVE_SYS_SHM_H
60 #   include <X11/extensions/XShm.h>
61 #endif
62 #ifdef DPMSINFO_IN_DPMS_H
63 #   include <X11/extensions/dpms.h>
64 #endif
65
66 #ifdef MODULE_NAME_IS_xvideo
67 #   include <X11/extensions/Xv.h>
68 #   include <X11/extensions/Xvlib.h>
69 #endif
70
71 #ifdef MODULE_NAME_IS_glx
72 #   include <GL/glx.h>
73 #endif
74
75 #ifdef HAVE_XINERAMA
76 #   include <X11/extensions/Xinerama.h>
77 #endif
78
79 #ifdef HAVE_X11_EXTENSIONS_XF86VMODE_H
80 #   include <X11/extensions/xf86vmode.h>
81 #endif
82
83 #include "xcommon.h"
84
85 /*****************************************************************************
86  * Local prototypes
87  *****************************************************************************/
88 int  E_(Activate)   ( vlc_object_t * );
89 void E_(Deactivate) ( vlc_object_t * );
90
91 static int  InitVideo      ( vout_thread_t * );
92 static void EndVideo       ( vout_thread_t * );
93 static void DisplayVideo   ( vout_thread_t *, picture_t * );
94 static int  ManageVideo    ( vout_thread_t * );
95 static int  Control        ( vout_thread_t *, int, va_list );
96
97 static int  InitDisplay    ( vout_thread_t * );
98
99 static int  CreateWindow   ( vout_thread_t *, x11_window_t * );
100 static void DestroyWindow  ( vout_thread_t *, x11_window_t * );
101
102 static int  NewPicture     ( vout_thread_t *, picture_t * );
103 static void FreePicture    ( vout_thread_t *, picture_t * );
104
105 static IMAGE_TYPE *CreateImage    ( vout_thread_t *,
106                                     Display *, EXTRA_ARGS, int, int );
107 #ifdef HAVE_SYS_SHM_H
108 static IMAGE_TYPE *CreateShmImage ( vout_thread_t *,
109                                     Display *, EXTRA_ARGS_SHM, int, int );
110 static vlc_bool_t b_shm = VLC_TRUE;
111 #endif
112
113 static void ToggleFullScreen      ( vout_thread_t * );
114
115 static void EnableXScreenSaver    ( vout_thread_t * );
116 static void DisableXScreenSaver   ( vout_thread_t * );
117
118 static void CreateCursor   ( vout_thread_t * );
119 static void DestroyCursor  ( vout_thread_t * );
120 static void ToggleCursor   ( vout_thread_t * );
121
122 #ifdef MODULE_NAME_IS_xvideo
123 static int  XVideoGetPort    ( vout_thread_t *, vlc_fourcc_t, vlc_fourcc_t * );
124 static void XVideoReleasePort( vout_thread_t *, int );
125 #endif
126
127 #ifdef MODULE_NAME_IS_x11
128 static void SetPalette     ( vout_thread_t *,
129                              uint16_t *, uint16_t *, uint16_t * );
130 #endif
131
132 static void TestNetWMSupport( vout_thread_t * );
133 static int ConvertKey( int );
134
135 static int WindowOnTop( vout_thread_t *, vlc_bool_t );
136
137 static int X11ErrorHandler( Display *, XErrorEvent * );
138
139 /*****************************************************************************
140  * Activate: allocate X11 video thread output method
141  *****************************************************************************
142  * This function allocate and initialize a X11 vout method. It uses some of the
143  * vout properties to choose the window size, and change them according to the
144  * actual properties of the display.
145  *****************************************************************************/
146 int E_(Activate) ( vlc_object_t *p_this )
147 {
148     vout_thread_t *p_vout = (vout_thread_t *)p_this;
149     char *        psz_display;
150     vlc_value_t   val;
151
152 #ifdef MODULE_NAME_IS_xvideo
153     char *       psz_chroma;
154     vlc_fourcc_t i_chroma = 0;
155     vlc_bool_t   b_chroma = 0;
156 #endif
157
158     p_vout->pf_init = InitVideo;
159     p_vout->pf_end = EndVideo;
160     p_vout->pf_manage = ManageVideo;
161     p_vout->pf_render = NULL;
162     p_vout->pf_display = DisplayVideo;
163     p_vout->pf_control = Control;
164
165     /* Allocate structure */
166     p_vout->p_sys = malloc( sizeof( vout_sys_t ) );
167     if( p_vout->p_sys == NULL )
168     {
169         msg_Err( p_vout, "out of memory" );
170         return VLC_ENOMEM;
171     }
172
173     vlc_mutex_init( p_vout, &p_vout->p_sys->lock );
174
175     /* Open display, using the "display" config variable or the DISPLAY
176      * environment variable */
177     psz_display = config_GetPsz( p_vout, MODULE_STRING "-display" );
178
179     p_vout->p_sys->p_display = XOpenDisplay( psz_display );
180
181     if( p_vout->p_sys->p_display == NULL )                          /* error */
182     {
183         msg_Err( p_vout, "cannot open display %s",
184                          XDisplayName( psz_display ) );
185         free( p_vout->p_sys );
186         if( psz_display ) free( psz_display );
187         return VLC_EGENERIC;
188     }
189     if( psz_display ) free( psz_display );
190
191     /* Replace error handler so we can intercept some non-fatal errors */
192     XSetErrorHandler( X11ErrorHandler );
193
194     /* Get a screen ID matching the XOpenDisplay return value */
195     p_vout->p_sys->i_screen = DefaultScreen( p_vout->p_sys->p_display );
196
197 #ifdef MODULE_NAME_IS_xvideo
198     psz_chroma = config_GetPsz( p_vout, "xvideo-chroma" );
199     if( psz_chroma )
200     {
201         if( strlen( psz_chroma ) >= 4 )
202         {
203             /* Do not use direct assignment because we are not sure of the
204              * alignment. */
205             memcpy(&i_chroma, psz_chroma, 4);
206             b_chroma = 1;
207         }
208
209         free( psz_chroma );
210     }
211
212     if( b_chroma )
213     {
214         msg_Dbg( p_vout, "forcing chroma 0x%.8x (%4.4s)",
215                  i_chroma, (char*)&i_chroma );
216     }
217     else
218     {
219         i_chroma = p_vout->render.i_chroma;
220     }
221
222     /* Check that we have access to an XVideo port providing this chroma */
223     p_vout->p_sys->i_xvport = XVideoGetPort( p_vout, VLC2X11_FOURCC(i_chroma),
224                                              &p_vout->output.i_chroma );
225     if( p_vout->p_sys->i_xvport < 0 )
226     {
227         /* If a specific chroma format was requested, then we don't try to
228          * be cleverer than the user. He knew pretty well what he wanted. */
229         if( b_chroma )
230         {
231             XCloseDisplay( p_vout->p_sys->p_display );
232             free( p_vout->p_sys );
233             return VLC_EGENERIC;
234         }
235
236         /* It failed, but it's not completely lost ! We try to open an
237          * XVideo port for an YUY2 picture. We'll need to do an YUV
238          * conversion, but at least it has got scaling. */
239         p_vout->p_sys->i_xvport =
240                         XVideoGetPort( p_vout, X11_FOURCC('Y','U','Y','2'),
241                                                &p_vout->output.i_chroma );
242         if( p_vout->p_sys->i_xvport < 0 )
243         {
244             /* It failed, but it's not completely lost ! We try to open an
245              * XVideo port for a simple 16bpp RGB picture. We'll need to do
246              * an YUV conversion, but at least it has got scaling. */
247             p_vout->p_sys->i_xvport =
248                             XVideoGetPort( p_vout, X11_FOURCC('R','V','1','6'),
249                                                    &p_vout->output.i_chroma );
250             if( p_vout->p_sys->i_xvport < 0 )
251             {
252                 XCloseDisplay( p_vout->p_sys->p_display );
253                 free( p_vout->p_sys );
254                 return VLC_EGENERIC;
255             }
256         }
257     }
258     p_vout->output.i_chroma = X112VLC_FOURCC(p_vout->output.i_chroma);
259 #endif
260
261     /* Create blank cursor (for mouse cursor autohiding) */
262     p_vout->p_sys->i_time_mouse_last_moved = mdate();
263     p_vout->p_sys->b_mouse_pointer_visible = 1;
264     CreateCursor( p_vout );
265
266     /* Set main window's size */
267     p_vout->p_sys->original_window.i_width = p_vout->i_window_width;
268     p_vout->p_sys->original_window.i_height = p_vout->i_window_height;
269     var_Create( p_vout, "video-title", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
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, &p_vout->p_sys->original_window ) )
273     {
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 );
278         return VLC_EGENERIC;
279     }
280
281     /* Open and initialize device. */
282     if( InitDisplay( p_vout ) )
283     {
284         msg_Err( p_vout, "cannot initialize X11 display" );
285         DestroyCursor( p_vout );
286         DestroyWindow( p_vout, &p_vout->p_sys->original_window );
287         XCloseDisplay( p_vout->p_sys->p_display );
288         free( p_vout->p_sys );
289         return VLC_EGENERIC;
290     }
291
292     /* Disable screen saver */
293     DisableXScreenSaver( p_vout );
294
295     /* Misc init */
296     p_vout->p_sys->b_altfullscreen = 0;
297     p_vout->p_sys->i_time_button_last_pressed = 0;
298
299     TestNetWMSupport( p_vout );
300
301     /* Variable to indicate if the window should be on top of others */
302     /* Trigger a callback right now */
303     var_Get( p_vout, "video-on-top", &val );
304     var_Set( p_vout, "video-on-top", val );
305
306     return VLC_SUCCESS;
307 }
308
309 /*****************************************************************************
310  * Deactivate: destroy X11 video thread output method
311  *****************************************************************************
312  * Terminate an output method created by Open
313  *****************************************************************************/
314 void E_(Deactivate) ( vlc_object_t *p_this )
315 {
316     vout_thread_t *p_vout = (vout_thread_t *)p_this;
317
318     /* If the fullscreen window is still open, close it */
319     if( p_vout->b_fullscreen )
320     {
321         ToggleFullScreen( p_vout );
322     }
323
324     /* Restore cursor if it was blanked */
325     if( !p_vout->p_sys->b_mouse_pointer_visible )
326     {
327         ToggleCursor( p_vout );
328     }
329
330 #ifdef MODULE_NAME_IS_x11
331     /* Destroy colormap */
332     if( XDefaultDepth(p_vout->p_sys->p_display, p_vout->p_sys->i_screen) == 8 )
333     {
334         XFreeColormap( p_vout->p_sys->p_display, p_vout->p_sys->colormap );
335     }
336 #elif defined(MODULE_NAME_IS_xvideo)
337     XVideoReleasePort( p_vout, p_vout->p_sys->i_xvport );
338 #endif
339
340     DestroyCursor( p_vout );
341     EnableXScreenSaver( p_vout );
342     DestroyWindow( p_vout, &p_vout->p_sys->original_window );
343
344     XCloseDisplay( p_vout->p_sys->p_display );
345
346     /* Destroy structure */
347     vlc_mutex_destroy( &p_vout->p_sys->lock );
348     free( p_vout->p_sys );
349 }
350
351 /*****************************************************************************
352  * InitVideo: initialize X11 video thread output method
353  *****************************************************************************
354  * This function create the XImages needed by the output thread. It is called
355  * at the beginning of the thread, but also each time the window is resized.
356  *****************************************************************************/
357 static int InitVideo( vout_thread_t *p_vout )
358 {
359     int i_index;
360     picture_t *p_pic;
361
362     I_OUTPUTPICTURES = 0;
363
364 #ifdef MODULE_NAME_IS_xvideo
365     /* Initialize the output structure; we already found an XVideo port,
366      * and the corresponding chroma we will be using. Since we can
367      * arbitrary scale, stick to the coordinates and aspect. */
368     p_vout->output.i_width  = p_vout->render.i_width;
369     p_vout->output.i_height = p_vout->render.i_height;
370     p_vout->output.i_aspect = p_vout->render.i_aspect;
371
372     p_vout->fmt_out = p_vout->fmt_in;
373     p_vout->fmt_out.i_chroma = p_vout->output.i_chroma;
374
375     switch( p_vout->output.i_chroma )
376     {
377         case VLC_FOURCC('R','V','1','5'):
378             p_vout->output.i_rmask = 0x001f;
379             p_vout->output.i_gmask = 0x07e0;
380             p_vout->output.i_bmask = 0xf800;
381             break;
382         case VLC_FOURCC('R','V','1','6'):
383             p_vout->output.i_rmask = 0x001f;
384             p_vout->output.i_gmask = 0x03e0;
385             p_vout->output.i_bmask = 0x7c00;
386             break;
387     }
388
389 #elif defined(MODULE_NAME_IS_x11)
390     /* Initialize the output structure: RGB with square pixels, whatever
391      * the input format is, since it's the only format we know */
392     switch( p_vout->p_sys->i_screen_depth )
393     {
394         case 8: /* FIXME: set the palette */
395             p_vout->output.i_chroma = VLC_FOURCC('R','G','B','2'); break;
396         case 15:
397             p_vout->output.i_chroma = VLC_FOURCC('R','V','1','5'); break;
398         case 16:
399             p_vout->output.i_chroma = VLC_FOURCC('R','V','1','6'); break;
400         case 24:
401         case 32:
402             p_vout->output.i_chroma = VLC_FOURCC('R','V','3','2'); break;
403         default:
404             msg_Err( p_vout, "unknown screen depth %i",
405                      p_vout->p_sys->i_screen_depth );
406             return VLC_SUCCESS;
407     }
408
409     vout_PlacePicture( p_vout, p_vout->p_sys->p_win->i_width,
410                        p_vout->p_sys->p_win->i_height,
411                        &i_index, &i_index,
412                        &p_vout->fmt_out.i_visible_width,
413                        &p_vout->fmt_out.i_visible_height );
414
415     p_vout->fmt_out.i_chroma = p_vout->output.i_chroma;
416
417     p_vout->output.i_width = p_vout->fmt_out.i_width =
418         p_vout->fmt_out.i_visible_width * p_vout->fmt_in.i_width /
419         p_vout->fmt_in.i_visible_width;
420     p_vout->output.i_height = p_vout->fmt_out.i_height =
421         p_vout->fmt_out.i_visible_height * p_vout->fmt_in.i_height /
422         p_vout->fmt_in.i_visible_height;
423     p_vout->fmt_out.i_x_offset =
424         p_vout->fmt_out.i_visible_width * p_vout->fmt_in.i_x_offset /
425         p_vout->fmt_in.i_visible_width;
426     p_vout->fmt_out.i_y_offset =
427         p_vout->fmt_out.i_visible_height * p_vout->fmt_in.i_y_offset /
428         p_vout->fmt_in.i_visible_height;
429
430     p_vout->fmt_out.i_sar_num = p_vout->fmt_out.i_sar_den = 1;
431     p_vout->output.i_aspect = p_vout->fmt_out.i_aspect =
432         p_vout->fmt_out.i_width * VOUT_ASPECT_FACTOR /p_vout->fmt_out.i_height;
433
434     msg_Dbg( p_vout, "x11 image size %ix%i (%i,%i,%ix%i)",
435              p_vout->fmt_out.i_width, p_vout->fmt_out.i_height,
436              p_vout->fmt_out.i_x_offset, p_vout->fmt_out.i_y_offset,
437              p_vout->fmt_out.i_visible_width,
438              p_vout->fmt_out.i_visible_height );
439 #endif
440
441     /* Try to initialize up to MAX_DIRECTBUFFERS direct buffers */
442     while( I_OUTPUTPICTURES < MAX_DIRECTBUFFERS )
443     {
444         p_pic = NULL;
445
446         /* Find an empty picture slot */
447         for( i_index = 0 ; i_index < VOUT_MAX_PICTURES ; i_index++ )
448         {
449           if( p_vout->p_picture[ i_index ].i_status == FREE_PICTURE )
450             {
451                 p_pic = p_vout->p_picture + i_index;
452                 break;
453             }
454         }
455
456         /* Allocate the picture */
457         if( p_pic == NULL || NewPicture( p_vout, p_pic ) )
458         {
459             break;
460         }
461
462         p_pic->i_status = DESTROYED_PICTURE;
463         p_pic->i_type   = DIRECT_PICTURE;
464
465         PP_OUTPUTPICTURE[ I_OUTPUTPICTURES ] = p_pic;
466
467         I_OUTPUTPICTURES++;
468     }
469
470     if( p_vout->output.i_chroma == VLC_FOURCC('Y','V','1','2') )
471     {
472         /* U and V inverted compared to I420
473          * Fixme: this should be handled by the vout core */
474         p_vout->output.i_chroma = VLC_FOURCC('I','4','2','0');
475         p_vout->fmt_out.i_chroma = VLC_FOURCC('I','4','2','0');
476     }
477
478     return VLC_SUCCESS;
479 }
480
481 /*****************************************************************************
482  * DisplayVideo: displays previously rendered output
483  *****************************************************************************
484  * This function sends the currently rendered image to X11 server.
485  * (The Xv extension takes care of "double-buffering".)
486  *****************************************************************************/
487 static void DisplayVideo( vout_thread_t *p_vout, picture_t *p_pic )
488 {
489     int i_width, i_height, i_x, i_y;
490
491     vout_PlacePicture( p_vout, p_vout->p_sys->p_win->i_width,
492                        p_vout->p_sys->p_win->i_height,
493                        &i_x, &i_y, &i_width, &i_height );
494
495     vlc_mutex_lock( &p_vout->p_sys->lock );
496
497 #ifdef HAVE_SYS_SHM_H
498     if( p_vout->p_sys->b_shm )
499     {
500         /* Display rendered image using shared memory extension */
501 #   ifdef MODULE_NAME_IS_xvideo
502         XvShmPutImage( p_vout->p_sys->p_display, p_vout->p_sys->i_xvport,
503                        p_vout->p_sys->p_win->video_window,
504                        p_vout->p_sys->p_win->gc, p_pic->p_sys->p_image,
505                        p_vout->fmt_out.i_x_offset,
506                        p_vout->fmt_out.i_y_offset,
507                        p_vout->fmt_out.i_visible_width,
508                        p_vout->fmt_out.i_visible_height,
509                        0 /*dest_x*/, 0 /*dest_y*/, i_width, i_height,
510                        False /* Don't put True here or you'll waste your CPU */ );
511 #   else
512         XShmPutImage( p_vout->p_sys->p_display,
513                       p_vout->p_sys->p_win->video_window,
514                       p_vout->p_sys->p_win->gc, p_pic->p_sys->p_image,
515                       p_vout->fmt_out.i_x_offset,
516                       p_vout->fmt_out.i_y_offset,
517                       0 /*dest_x*/, 0 /*dest_y*/,
518                       p_vout->fmt_out.i_visible_width,
519                       p_vout->fmt_out.i_visible_height,
520                       False /* Don't put True here ! */ );
521 #   endif
522     }
523     else
524 #endif /* HAVE_SYS_SHM_H */
525     {
526         /* Use standard XPutImage -- this is gonna be slow ! */
527 #ifdef MODULE_NAME_IS_xvideo
528         XvPutImage( p_vout->p_sys->p_display, p_vout->p_sys->i_xvport,
529                     p_vout->p_sys->p_win->video_window,
530                     p_vout->p_sys->p_win->gc, p_pic->p_sys->p_image,
531                     p_vout->fmt_out.i_x_offset,
532                     p_vout->fmt_out.i_y_offset,
533                     p_vout->fmt_out.i_visible_width,
534                     p_vout->fmt_out.i_visible_height,
535                     0 /*dest_x*/, 0 /*dest_y*/, i_width, i_height );
536 #else
537         XPutImage( p_vout->p_sys->p_display,
538                    p_vout->p_sys->p_win->video_window,
539                    p_vout->p_sys->p_win->gc, p_pic->p_sys->p_image,
540                    p_vout->fmt_out.i_x_offset,
541                    p_vout->fmt_out.i_y_offset,
542                    0 /*dest_x*/, 0 /*dest_y*/,
543                    p_vout->fmt_out.i_visible_width,
544                    p_vout->fmt_out.i_visible_height );
545 #endif
546     }
547
548     /* Make sure the command is sent now - do NOT use XFlush !*/
549     XSync( p_vout->p_sys->p_display, False );
550
551     vlc_mutex_unlock( &p_vout->p_sys->lock );
552 }
553
554 /*****************************************************************************
555  * ManageVideo: handle X11 events
556  *****************************************************************************
557  * This function should be called regularly by video output thread. It manages
558  * X11 events and allows window resizing. It returns a non null value on
559  * error.
560  *****************************************************************************/
561 static int ManageVideo( vout_thread_t *p_vout )
562 {
563     XEvent      xevent;                                         /* X11 event */
564     vlc_value_t val;
565
566     vlc_mutex_lock( &p_vout->p_sys->lock );
567
568     /* Handle events from the owner window */
569     if( p_vout->p_sys->p_win->owner_window )
570     {
571         while( XCheckWindowEvent( p_vout->p_sys->p_display,
572                                   p_vout->p_sys->p_win->owner_window,
573                                   StructureNotifyMask, &xevent ) == True )
574         {
575             /* ConfigureNotify event: prepare  */
576             if( xevent.type == ConfigureNotify )
577             {
578                 /* Update dimensions */
579                 XResizeWindow( p_vout->p_sys->p_display,
580                                p_vout->p_sys->p_win->base_window,
581                                xevent.xconfigure.width,
582                                xevent.xconfigure.height );
583             }
584         }
585     }
586
587     /* Handle X11 events: ConfigureNotify events are parsed to know if the
588      * output window's size changed, MapNotify and UnmapNotify to know if the
589      * window is mapped (and if the display is useful), and ClientMessages
590      * to intercept window destruction requests */
591
592     while( XCheckWindowEvent( p_vout->p_sys->p_display,
593                               p_vout->p_sys->p_win->base_window,
594                               StructureNotifyMask | KeyPressMask |
595                               ButtonPressMask | ButtonReleaseMask |
596                               PointerMotionMask | Button1MotionMask , &xevent )
597            == True )
598     {
599         /* ConfigureNotify event: prepare  */
600         if( xevent.type == ConfigureNotify )
601         {
602             if( (unsigned int)xevent.xconfigure.width
603                    != p_vout->p_sys->p_win->i_width
604               || (unsigned int)xevent.xconfigure.height
605                     != p_vout->p_sys->p_win->i_height )
606             {
607                 /* Update dimensions */
608                 p_vout->i_changes |= VOUT_SIZE_CHANGE;
609                 p_vout->p_sys->p_win->i_width = xevent.xconfigure.width;
610                 p_vout->p_sys->p_win->i_height = xevent.xconfigure.height;
611             }
612         }
613         /* Keyboard event */
614         else if( xevent.type == KeyPress )
615         {
616             unsigned int state = xevent.xkey.state;
617             KeySym x_key_symbol;
618             char i_key;                                   /* ISO Latin-1 key */
619
620             /* We may have keys like F1 trough F12, ESC ... */
621             x_key_symbol = XKeycodeToKeysym( p_vout->p_sys->p_display,
622                                              xevent.xkey.keycode, 0 );
623             val.i_int = ConvertKey( (int)x_key_symbol );
624
625             xevent.xkey.state &= ~ShiftMask;
626             xevent.xkey.state &= ~ControlMask;
627             xevent.xkey.state &= ~Mod1Mask;
628
629             if( !val.i_int &&
630                 XLookupString( &xevent.xkey, &i_key, 1, NULL, NULL ) )
631             {
632                 /* "Normal Keys"
633                  * The reason why I use this instead of XK_0 is that
634                  * with XLookupString, we don't have to care about
635                  * keymaps. */
636                 val.i_int = i_key;
637             }
638
639             if( val.i_int )
640             {
641                 if( state & ShiftMask )
642                 {
643                     val.i_int |= KEY_MODIFIER_SHIFT;
644                 }
645                 if( state & ControlMask )
646                 {
647                     val.i_int |= KEY_MODIFIER_CTRL;
648                 }
649                 if( state & Mod1Mask )
650                 {
651                     val.i_int |= KEY_MODIFIER_ALT;
652                 }
653                 var_Set( p_vout->p_vlc, "key-pressed", val );
654             }
655         }
656         /* Mouse click */
657         else if( xevent.type == ButtonPress )
658         {
659             switch( ((XButtonEvent *)&xevent)->button )
660             {
661                 case Button1:
662                     var_Get( p_vout, "mouse-button-down", &val );
663                     val.i_int |= 1;
664                     var_Set( p_vout, "mouse-button-down", val );
665
666                     /* detect double-clicks */
667                     if( ( ((XButtonEvent *)&xevent)->time -
668                           p_vout->p_sys->i_time_button_last_pressed ) < 300 )
669                     {
670                         p_vout->i_changes |= VOUT_FULLSCREEN_CHANGE;
671                     }
672
673                     p_vout->p_sys->i_time_button_last_pressed =
674                         ((XButtonEvent *)&xevent)->time;
675                     break;
676                 case Button2:
677                     var_Get( p_vout, "mouse-button-down", &val );
678                     val.i_int |= 2;
679                     var_Set( p_vout, "mouse-button-down", val );
680                     break;
681
682                 case Button3:
683                     var_Get( p_vout, "mouse-button-down", &val );
684                     val.i_int |= 4;
685                     var_Set( p_vout, "mouse-button-down", val );
686                     break;
687
688                 case Button4:
689                     var_Get( p_vout, "mouse-button-down", &val );
690                     val.i_int |= 8;
691                     var_Set( p_vout, "mouse-button-down", val );
692                     break;
693
694                 case Button5:
695                     var_Get( p_vout, "mouse-button-down", &val );
696                     val.i_int |= 16;
697                     var_Set( p_vout, "mouse-button-down", val );
698                     break;
699             }
700         }
701         /* Mouse release */
702         else if( xevent.type == ButtonRelease )
703         {
704             switch( ((XButtonEvent *)&xevent)->button )
705             {
706                 case Button1:
707                     var_Get( p_vout, "mouse-button-down", &val );
708                     val.i_int &= ~1;
709                     var_Set( p_vout, "mouse-button-down", val );
710
711                     val.b_bool = VLC_TRUE;
712                     var_Set( p_vout, "mouse-clicked", val );
713                     break;
714
715                 case Button2:
716                     {
717                         playlist_t *p_playlist;
718
719                         var_Get( p_vout, "mouse-button-down", &val );
720                         val.i_int &= ~2;
721                         var_Set( p_vout, "mouse-button-down", val );
722
723                         p_playlist = vlc_object_find( p_vout,
724                                                       VLC_OBJECT_PLAYLIST,
725                                                       FIND_ANYWHERE );
726                         if( p_playlist != NULL )
727                         {
728                             vlc_value_t val;
729                             var_Get( p_playlist, "intf-show", &val );
730                             val.b_bool = !val.b_bool;
731                             var_Set( p_playlist, "intf-show", val );
732                             vlc_object_release( p_playlist );
733                         }
734                     }
735                     break;
736
737                 case Button3:
738                     {
739                         intf_thread_t *p_intf;
740                         playlist_t *p_playlist;
741
742                         var_Get( p_vout, "mouse-button-down", &val );
743                         val.i_int &= ~4;
744                         var_Set( p_vout, "mouse-button-down", val );
745                         p_intf = vlc_object_find( p_vout, VLC_OBJECT_INTF,
746                                                           FIND_ANYWHERE );
747                         if( p_intf )
748                         {
749                             p_intf->b_menu_change = 1;
750                             vlc_object_release( p_intf );
751                         }
752
753                         p_playlist = vlc_object_find( p_vout,
754                                                       VLC_OBJECT_PLAYLIST,
755                                                       FIND_ANYWHERE );
756                         if( p_playlist != NULL )
757                         {
758                             vlc_value_t val; val.b_bool = VLC_TRUE;
759                             var_Set( p_playlist, "intf-popupmenu", val );
760                             vlc_object_release( p_playlist );
761                         }
762                     }
763                     break;
764
765                 case Button4:
766                     var_Get( p_vout, "mouse-button-down", &val );
767                     val.i_int &= ~8;
768                     var_Set( p_vout, "mouse-button-down", val );
769                     break;
770
771                 case Button5:
772                     var_Get( p_vout, "mouse-button-down", &val );
773                     val.i_int &= ~16;
774                     var_Set( p_vout, "mouse-button-down", val );
775                     break;
776
777             }
778         }
779         /* Mouse move */
780         else if( xevent.type == MotionNotify )
781         {
782             int i_width, i_height, i_x, i_y;
783             vlc_value_t val;
784
785             /* somewhat different use for vout_PlacePicture:
786              * here the values are needed to give to mouse coordinates
787              * in the original picture space */
788             vout_PlacePicture( p_vout, p_vout->p_sys->p_win->i_width,
789                                p_vout->p_sys->p_win->i_height,
790                                &i_x, &i_y, &i_width, &i_height );
791
792             val.i_int = ( xevent.xmotion.x - i_x ) *
793                 p_vout->fmt_in.i_visible_width / i_width +
794                 p_vout->fmt_in.i_x_offset;
795             var_Set( p_vout, "mouse-x", val );
796             val.i_int = ( xevent.xmotion.y - i_y ) *
797                 p_vout->fmt_in.i_visible_height / i_height +
798                 p_vout->fmt_in.i_y_offset;
799             var_Set( p_vout, "mouse-y", val );
800
801             val.b_bool = VLC_TRUE;
802             var_Set( p_vout, "mouse-moved", val );
803
804             p_vout->p_sys->i_time_mouse_last_moved = mdate();
805             if( ! p_vout->p_sys->b_mouse_pointer_visible )
806             {
807                 ToggleCursor( p_vout );
808             }
809         }
810         else if( xevent.type == ReparentNotify /* XXX: why do we get this? */
811                   || xevent.type == MapNotify
812                   || xevent.type == UnmapNotify )
813         {
814             /* Ignore these events */
815         }
816         else /* Other events */
817         {
818             msg_Warn( p_vout, "unhandled event %d received", xevent.type );
819         }
820     }
821
822     /* Handle events for video output sub-window */
823     while( XCheckWindowEvent( p_vout->p_sys->p_display,
824                               p_vout->p_sys->p_win->video_window,
825                               ExposureMask, &xevent ) == True )
826     {
827         /* Window exposed (only handled if stream playback is paused) */
828         if( xevent.type == Expose )
829         {
830             if( ((XExposeEvent *)&xevent)->count == 0 )
831             {
832                 /* (if this is the last a collection of expose events...) */
833 #if 0
834                 if( p_vout->p_vlc->p_input_bank->pp_input[0] != NULL )
835                 {
836                     if( PAUSE_S == p_vout->p_vlc->p_input_bank->pp_input[0]
837                                                    ->stream.control.i_status )
838                     {
839                         /* XVideoDisplay( p_vout )*/;
840                     }
841                 }
842 #endif
843             }
844         }
845     }
846
847     /* ClientMessage event - only WM_PROTOCOLS with WM_DELETE_WINDOW data
848      * are handled - according to the man pages, the format is always 32
849      * in this case */
850     while( XCheckTypedEvent( p_vout->p_sys->p_display,
851                              ClientMessage, &xevent ) )
852     {
853         if( (xevent.xclient.message_type == p_vout->p_sys->p_win->wm_protocols)
854                && ((Atom)xevent.xclient.data.l[0]
855                      == p_vout->p_sys->p_win->wm_delete_window ) )
856         {
857             /* the user wants to close the window */
858             playlist_t * p_playlist =
859                 (playlist_t *)vlc_object_find( p_vout, VLC_OBJECT_PLAYLIST,
860                                                FIND_ANYWHERE );
861             if( p_playlist != NULL )
862             {
863                 playlist_Stop( p_playlist );
864                 vlc_object_release( p_playlist );
865             }
866         }
867     }
868
869     /*
870      * Fullscreen Change
871      */
872     if ( p_vout->i_changes & VOUT_FULLSCREEN_CHANGE )
873     {
874         vlc_value_t val;
875
876         /* Update the object variable and trigger callback */
877         val.b_bool = !p_vout->b_fullscreen;
878         var_Set( p_vout, "fullscreen", val );
879
880         ToggleFullScreen( p_vout );
881         p_vout->i_changes &= ~VOUT_FULLSCREEN_CHANGE;
882     }
883
884     if( p_vout->i_changes & VOUT_CROP_CHANGE ||
885         p_vout->i_changes & VOUT_ASPECT_CHANGE )
886     {
887         p_vout->i_changes &= ~VOUT_CROP_CHANGE;
888         p_vout->i_changes &= ~VOUT_ASPECT_CHANGE;
889
890         p_vout->fmt_out.i_x_offset = p_vout->fmt_in.i_x_offset;
891         p_vout->fmt_out.i_y_offset = p_vout->fmt_in.i_y_offset;
892         p_vout->fmt_out.i_visible_width = p_vout->fmt_in.i_visible_width;
893         p_vout->fmt_out.i_visible_height = p_vout->fmt_in.i_visible_height;
894         p_vout->fmt_out.i_aspect = p_vout->fmt_in.i_aspect;
895         p_vout->fmt_out.i_sar_num = p_vout->fmt_in.i_sar_num;
896         p_vout->fmt_out.i_sar_den = p_vout->fmt_in.i_sar_den;
897         p_vout->output.i_aspect = p_vout->fmt_in.i_aspect;
898
899         p_vout->i_changes |= VOUT_SIZE_CHANGE;
900     }
901
902     /*
903      * Size change
904      *
905      * (Needs to be placed after VOUT_FULLSREEN_CHANGE because we can activate
906      *  the size flag inside the fullscreen routine)
907      */
908     if( p_vout->i_changes & VOUT_SIZE_CHANGE )
909     {
910         int i_width, i_height, i_x, i_y;
911
912         p_vout->i_changes &= ~VOUT_SIZE_CHANGE;
913
914 #ifdef MODULE_NAME_IS_x11
915         /* We need to signal the vout thread about the size change because it
916          * is doing the rescaling */
917         p_vout->i_changes |= VOUT_SIZE_CHANGE;
918 #endif
919
920         vout_PlacePicture( p_vout, p_vout->p_sys->p_win->i_width,
921                            p_vout->p_sys->p_win->i_height,
922                            &i_x, &i_y, &i_width, &i_height );
923
924         XMoveResizeWindow( p_vout->p_sys->p_display,
925                            p_vout->p_sys->p_win->video_window,
926                            i_x, i_y, i_width, i_height );
927     }
928
929     /* Autohide Cursour */
930     if( mdate() - p_vout->p_sys->i_time_mouse_last_moved > 2000000 )
931     {
932         /* Hide the mouse automatically */
933         if( p_vout->p_sys->b_mouse_pointer_visible )
934         {
935             ToggleCursor( p_vout );
936         }
937     }
938
939     vlc_mutex_unlock( &p_vout->p_sys->lock );
940
941     return 0;
942 }
943
944 /*****************************************************************************
945  * EndVideo: terminate X11 video thread output method
946  *****************************************************************************
947  * Destroy the X11 XImages created by Init. It is called at the end of
948  * the thread, but also each time the window is resized.
949  *****************************************************************************/
950 static void EndVideo( vout_thread_t *p_vout )
951 {
952     int i_index;
953
954     /* Free the direct buffers we allocated */
955     for( i_index = I_OUTPUTPICTURES ; i_index ; )
956     {
957         i_index--;
958         FreePicture( p_vout, PP_OUTPUTPICTURE[ i_index ] );
959     }
960 }
961
962 /* following functions are local */
963
964 /*****************************************************************************
965  * CreateWindow: open and set-up X11 main window
966  *****************************************************************************/
967 static int CreateWindow( vout_thread_t *p_vout, x11_window_t *p_win )
968 {
969     XSizeHints              xsize_hints;
970     XSetWindowAttributes    xwindow_attributes;
971     XGCValues               xgcvalues;
972     XEvent                  xevent;
973
974     vlc_bool_t              b_expose = VLC_FALSE;
975     vlc_bool_t              b_configure_notify = VLC_FALSE;
976     vlc_bool_t              b_map_notify = VLC_FALSE;
977     vlc_value_t             val;
978
979     /* Prepare window manager hints and properties */
980     p_win->wm_protocols =
981              XInternAtom( p_vout->p_sys->p_display, "WM_PROTOCOLS", True );
982     p_win->wm_delete_window =
983              XInternAtom( p_vout->p_sys->p_display, "WM_DELETE_WINDOW", True );
984
985     /* Never have a 0-pixel-wide window */
986     xsize_hints.min_width = 2;
987     xsize_hints.min_height = 1;
988
989     /* Prepare window attributes */
990     xwindow_attributes.backing_store = Always;       /* save the hidden part */
991     xwindow_attributes.background_pixel = BlackPixel(p_vout->p_sys->p_display,
992                                                      p_vout->p_sys->i_screen);
993     xwindow_attributes.event_mask = ExposureMask | StructureNotifyMask;
994
995     if( !p_vout->b_fullscreen )
996     {
997         p_win->owner_window =
998             (Window)vout_RequestWindow( p_vout, &p_win->i_x, &p_win->i_y,
999                                         &p_win->i_width, &p_win->i_height );
1000
1001         xsize_hints.base_width  = xsize_hints.width = p_win->i_width;
1002         xsize_hints.base_height = xsize_hints.height = p_win->i_height;
1003         xsize_hints.flags       = PSize | PMinSize;
1004
1005         if( p_win->i_x >=0 || p_win->i_y >= 0 )
1006         {
1007             xsize_hints.x = p_win->i_x;
1008             xsize_hints.y = p_win->i_y;
1009             xsize_hints.flags |= PPosition;
1010         }
1011     }
1012     else
1013     {
1014         /* Fullscreen window size and position */
1015         p_win->owner_window = 0;
1016         p_win->i_x = p_win->i_y = 0;
1017         p_win->i_width =
1018             DisplayWidth( p_vout->p_sys->p_display, p_vout->p_sys->i_screen );
1019         p_win->i_height =
1020             DisplayHeight( p_vout->p_sys->p_display, p_vout->p_sys->i_screen );
1021     }
1022
1023     if( !p_win->owner_window )
1024     {
1025         /* Create the window and set hints - the window must receive
1026          * ConfigureNotify events, and until it is displayed, Expose and
1027          * MapNotify events. */
1028
1029         p_win->base_window =
1030             XCreateWindow( p_vout->p_sys->p_display,
1031                            DefaultRootWindow( p_vout->p_sys->p_display ),
1032                            p_win->i_x, p_win->i_y,
1033                            p_win->i_width, p_win->i_height,
1034                            0,
1035                            0, InputOutput, 0,
1036                            CWBackingStore | CWBackPixel | CWEventMask,
1037                            &xwindow_attributes );
1038
1039         if( !p_vout->b_fullscreen )
1040         {
1041             /* Set window manager hints and properties: size hints, command,
1042              * window's name, and accepted protocols */
1043             XSetWMNormalHints( p_vout->p_sys->p_display,
1044                                p_win->base_window, &xsize_hints );
1045             XSetCommand( p_vout->p_sys->p_display, p_win->base_window,
1046                          p_vout->p_vlc->ppsz_argv, p_vout->p_vlc->i_argc );
1047
1048             if( !var_GetBool( p_vout, "video-deco") )
1049             {
1050                 Atom prop;
1051                 mwmhints_t mwmhints;
1052
1053                 mwmhints.flags = MWM_HINTS_DECORATIONS;
1054                 mwmhints.decorations = False;
1055
1056                 prop = XInternAtom( p_vout->p_sys->p_display, "_MOTIF_WM_HINTS",
1057                                     False );
1058
1059                 XChangeProperty( p_vout->p_sys->p_display,
1060                                  p_win->base_window,
1061                                  prop, prop, 32, PropModeReplace,
1062                                  (unsigned char *)&mwmhints,
1063                                  PROP_MWM_HINTS_ELEMENTS );
1064             }
1065             else
1066             {
1067                  var_Get( p_vout, "video-title", &val );
1068                  if( !val.psz_string || !*val.psz_string )
1069                  {
1070                     XStoreName( p_vout->p_sys->p_display, p_win->base_window,
1071 #ifdef MODULE_NAME_IS_x11
1072                                 VOUT_TITLE " (X11 output)"
1073 #elif defined(MODULE_NAME_IS_glx)
1074                                 VOUT_TITLE " (GLX output)"
1075 #else
1076                                 VOUT_TITLE " (XVideo output)"
1077 #endif
1078                       );
1079                 }
1080                 else
1081                 {
1082                     XStoreName( p_vout->p_sys->p_display,
1083                                p_win->base_window, val.psz_string );
1084                 }
1085             }
1086         }
1087     }
1088     else
1089     {
1090         Window dummy1;
1091         unsigned int dummy2, dummy3;
1092
1093         /* Select events we are interested in. */
1094         XSelectInput( p_vout->p_sys->p_display, p_win->owner_window,
1095                       StructureNotifyMask );
1096
1097         /* Get the parent window's geometry information */
1098         XGetGeometry( p_vout->p_sys->p_display, p_win->owner_window,
1099                       &dummy1, &dummy2, &dummy3,
1100                       &p_win->i_width,
1101                       &p_win->i_height,
1102                       &dummy2, &dummy3 );
1103
1104         /* We are already configured */
1105         b_configure_notify = VLC_TRUE;
1106
1107         /* From man XSelectInput: only one client at a time can select a
1108          * ButtonPress event, so we need to open a new window anyway. */
1109         p_win->base_window =
1110             XCreateWindow( p_vout->p_sys->p_display,
1111                            p_win->owner_window,
1112                            0, 0,
1113                            p_win->i_width, p_win->i_height,
1114                            0,
1115                            0, CopyFromParent, 0,
1116                            CWBackingStore | CWBackPixel | CWEventMask,
1117                            &xwindow_attributes );
1118     }
1119
1120     if( (p_win->wm_protocols == None)        /* use WM_DELETE_WINDOW */
1121         || (p_win->wm_delete_window == None)
1122         || !XSetWMProtocols( p_vout->p_sys->p_display, p_win->base_window,
1123                              &p_win->wm_delete_window, 1 ) )
1124     {
1125         /* WM_DELETE_WINDOW is not supported by window manager */
1126         msg_Warn( p_vout, "missing or bad window manager" );
1127     }
1128
1129     /* Creation of a graphic context that doesn't generate a GraphicsExpose
1130      * event when using functions like XCopyArea */
1131     xgcvalues.graphics_exposures = False;
1132     p_win->gc = XCreateGC( p_vout->p_sys->p_display,
1133                            p_win->base_window,
1134                            GCGraphicsExposures, &xgcvalues );
1135
1136     /* Send orders to server, and wait until window is displayed - three
1137      * events must be received: a MapNotify event, an Expose event allowing
1138      * drawing in the window, and a ConfigureNotify to get the window
1139      * dimensions. Once those events have been received, only
1140      * ConfigureNotify events need to be received. */
1141     XMapWindow( p_vout->p_sys->p_display, p_win->base_window );
1142     do
1143     {
1144         XWindowEvent( p_vout->p_sys->p_display, p_win->base_window,
1145                       SubstructureNotifyMask | StructureNotifyMask |
1146                       ExposureMask, &xevent);
1147         if( (xevent.type == Expose)
1148             && (xevent.xexpose.window == p_win->base_window) )
1149         {
1150             b_expose = VLC_TRUE;
1151             /* ConfigureNotify isn't sent if there isn't a window manager.
1152              * Expose should be the last event to be received so it should
1153              * be fine to assume we won't receive it anymore. */
1154             b_configure_notify = VLC_TRUE;
1155         }
1156         else if( (xevent.type == MapNotify)
1157                  && (xevent.xmap.window == p_win->base_window) )
1158         {
1159             b_map_notify = VLC_TRUE;
1160         }
1161         else if( (xevent.type == ConfigureNotify)
1162                  && (xevent.xconfigure.window == p_win->base_window) )
1163         {
1164             b_configure_notify = VLC_TRUE;
1165             p_win->i_width = xevent.xconfigure.width;
1166             p_win->i_height = xevent.xconfigure.height;
1167         }
1168     } while( !( b_expose && b_configure_notify && b_map_notify ) );
1169
1170     XSelectInput( p_vout->p_sys->p_display, p_win->base_window,
1171                   StructureNotifyMask | KeyPressMask |
1172                   ButtonPressMask | ButtonReleaseMask |
1173                   PointerMotionMask );
1174
1175 #ifdef MODULE_NAME_IS_x11
1176     if( XDefaultDepth(p_vout->p_sys->p_display, p_vout->p_sys->i_screen) == 8 )
1177     {
1178         /* Allocate a new palette */
1179         p_vout->p_sys->colormap =
1180             XCreateColormap( p_vout->p_sys->p_display,
1181                              DefaultRootWindow( p_vout->p_sys->p_display ),
1182                              DefaultVisual( p_vout->p_sys->p_display,
1183                                             p_vout->p_sys->i_screen ),
1184                              AllocAll );
1185
1186         xwindow_attributes.colormap = p_vout->p_sys->colormap;
1187         XChangeWindowAttributes( p_vout->p_sys->p_display, p_win->base_window,
1188                                  CWColormap, &xwindow_attributes );
1189     }
1190 #endif
1191
1192     /* Create video output sub-window. */
1193     p_win->video_window =  XCreateSimpleWindow(
1194                                       p_vout->p_sys->p_display,
1195                                       p_win->base_window, 0, 0,
1196                                       p_win->i_width, p_win->i_height,
1197                                       0,
1198                                       BlackPixel( p_vout->p_sys->p_display,
1199                                                   p_vout->p_sys->i_screen ),
1200                                       WhitePixel( p_vout->p_sys->p_display,
1201                                                   p_vout->p_sys->i_screen ) );
1202
1203     XSetWindowBackground( p_vout->p_sys->p_display, p_win->video_window,
1204                           BlackPixel( p_vout->p_sys->p_display,
1205                                       p_vout->p_sys->i_screen ) );
1206
1207     XMapWindow( p_vout->p_sys->p_display, p_win->video_window );
1208     XSelectInput( p_vout->p_sys->p_display, p_win->video_window,
1209                   ExposureMask );
1210
1211     /* make sure the video window will be centered in the next ManageVideo() */
1212     p_vout->i_changes |= VOUT_SIZE_CHANGE;
1213
1214     /* If the cursor was formerly blank than blank it again */
1215     if( !p_vout->p_sys->b_mouse_pointer_visible )
1216     {
1217         ToggleCursor( p_vout );
1218         ToggleCursor( p_vout );
1219     }
1220
1221     /* Do NOT use XFlush here ! */
1222     XSync( p_vout->p_sys->p_display, False );
1223
1224     /* At this stage, the window is open, displayed, and ready to
1225      * receive data */
1226     p_vout->p_sys->p_win = p_win;
1227
1228     return VLC_SUCCESS;
1229 }
1230
1231 /*****************************************************************************
1232  * DestroyWindow: destroy the window
1233  *****************************************************************************
1234  *
1235  *****************************************************************************/
1236 static void DestroyWindow( vout_thread_t *p_vout, x11_window_t *p_win )
1237 {
1238     /* Do NOT use XFlush here ! */
1239     XSync( p_vout->p_sys->p_display, False );
1240
1241     if( p_win->video_window != None )
1242         XDestroyWindow( p_vout->p_sys->p_display, p_win->video_window );
1243
1244     XFreeGC( p_vout->p_sys->p_display, p_win->gc );
1245
1246     XUnmapWindow( p_vout->p_sys->p_display, p_win->base_window );
1247     XDestroyWindow( p_vout->p_sys->p_display, p_win->base_window );
1248
1249     if( p_win->owner_window )
1250         vout_ReleaseWindow( p_vout, (void *)p_win->owner_window );
1251 }
1252
1253 /*****************************************************************************
1254  * NewPicture: allocate a picture
1255  *****************************************************************************
1256  * Returns 0 on success, -1 otherwise
1257  *****************************************************************************/
1258 static int NewPicture( vout_thread_t *p_vout, picture_t *p_pic )
1259 {
1260 #ifndef MODULE_NAME_IS_glx
1261
1262 #ifdef MODULE_NAME_IS_xvideo
1263     int i_plane;
1264 #endif
1265
1266     /* We know the chroma, allocate a buffer which will be used
1267      * directly by the decoder */
1268     p_pic->p_sys = malloc( sizeof( picture_sys_t ) );
1269
1270     if( p_pic->p_sys == NULL )
1271     {
1272         return -1;
1273     }
1274
1275     /* Fill in picture_t fields */
1276     vout_InitPicture( VLC_OBJECT(p_vout), p_pic, p_vout->output.i_chroma,
1277                       p_vout->output.i_width, p_vout->output.i_height,
1278                       p_vout->output.i_aspect );
1279
1280 #ifdef HAVE_SYS_SHM_H
1281     if( p_vout->p_sys->b_shm )
1282     {
1283         /* Create image using XShm extension */
1284         p_pic->p_sys->p_image =
1285             CreateShmImage( p_vout, p_vout->p_sys->p_display,
1286 #   ifdef MODULE_NAME_IS_xvideo
1287                             p_vout->p_sys->i_xvport, 
1288                             VLC2X11_FOURCC(p_vout->output.i_chroma),
1289 #   else
1290                             p_vout->p_sys->p_visual,
1291                             p_vout->p_sys->i_screen_depth,
1292 #   endif
1293                             &p_pic->p_sys->shminfo,
1294                             p_vout->output.i_width, p_vout->output.i_height );
1295     }
1296
1297     if( !p_vout->p_sys->b_shm || !p_pic->p_sys->p_image )
1298 #endif /* HAVE_SYS_SHM_H */
1299     {
1300         /* Create image without XShm extension */
1301         p_pic->p_sys->p_image =
1302             CreateImage( p_vout, p_vout->p_sys->p_display,
1303 #ifdef MODULE_NAME_IS_xvideo
1304                          p_vout->p_sys->i_xvport, 
1305                          VLC2X11_FOURCC(p_vout->output.i_chroma),
1306                          p_pic->format.i_bits_per_pixel,
1307 #else
1308                          p_vout->p_sys->p_visual,
1309                          p_vout->p_sys->i_screen_depth,
1310                          p_vout->p_sys->i_bytes_per_pixel,
1311 #endif
1312                          p_vout->output.i_width, p_vout->output.i_height );
1313
1314 #ifdef HAVE_SYS_SHM_H
1315         if( p_pic->p_sys->p_image && p_vout->p_sys->b_shm )
1316         {
1317             msg_Warn( p_vout, "couldn't create SHM image, disabling SHM" );
1318             p_vout->p_sys->b_shm = VLC_FALSE;
1319         }
1320 #endif /* HAVE_SYS_SHM_H */
1321     }
1322
1323     if( p_pic->p_sys->p_image == NULL )
1324     {
1325         free( p_pic->p_sys );
1326         return -1;
1327     }
1328
1329     switch( p_vout->output.i_chroma )
1330     {
1331 #ifdef MODULE_NAME_IS_xvideo
1332         case VLC_FOURCC('I','4','2','0'):
1333         case VLC_FOURCC('Y','V','1','2'):
1334         case VLC_FOURCC('Y','2','1','1'):
1335         case VLC_FOURCC('Y','U','Y','2'):
1336         case VLC_FOURCC('U','Y','V','Y'):
1337         case VLC_FOURCC('R','V','1','5'):
1338         case VLC_FOURCC('R','V','1','6'):
1339         case VLC_FOURCC('R','V','2','4'): /* Fixme: pixel pitch == 4 ? */
1340         case VLC_FOURCC('R','V','3','2'):
1341
1342             for( i_plane = 0; i_plane < p_pic->p_sys->p_image->num_planes;
1343                  i_plane++ )
1344             {
1345                 p_pic->p[i_plane].p_pixels = p_pic->p_sys->p_image->data
1346                     + p_pic->p_sys->p_image->offsets[i_plane];
1347                 p_pic->p[i_plane].i_pitch =
1348                     p_pic->p_sys->p_image->pitches[i_plane];
1349             }
1350             if( p_vout->output.i_chroma == VLC_FOURCC('Y','V','1','2') )
1351             {
1352                 /* U and V inverted compared to I420
1353                  * Fixme: this should be handled by the vout core */
1354                 p_pic->U_PIXELS = p_pic->p_sys->p_image->data
1355                     + p_pic->p_sys->p_image->offsets[2];
1356                 p_pic->V_PIXELS = p_pic->p_sys->p_image->data
1357                     + p_pic->p_sys->p_image->offsets[1];
1358             }
1359             break;
1360
1361 #else
1362         case VLC_FOURCC('R','G','B','2'):
1363         case VLC_FOURCC('R','V','1','6'):
1364         case VLC_FOURCC('R','V','1','5'):
1365         case VLC_FOURCC('R','V','2','4'):
1366         case VLC_FOURCC('R','V','3','2'):
1367
1368             p_pic->p->i_lines = p_pic->p_sys->p_image->height;
1369             p_pic->p->i_visible_lines = p_pic->p_sys->p_image->height;
1370             p_pic->p->p_pixels = p_pic->p_sys->p_image->data
1371                                   + p_pic->p_sys->p_image->xoffset;
1372             p_pic->p->i_pitch = p_pic->p_sys->p_image->bytes_per_line;
1373
1374             /* p_pic->p->i_pixel_pitch = 4 for RV24 but this should be set
1375              * properly by vout_InitPicture() */
1376             p_pic->p->i_visible_pitch = p_pic->p->i_pixel_pitch
1377                                          * p_pic->p_sys->p_image->width;
1378             break;
1379 #endif
1380
1381         default:
1382             /* Unknown chroma, tell the guy to get lost */
1383             IMAGE_FREE( p_pic->p_sys->p_image );
1384             free( p_pic->p_sys );
1385             msg_Err( p_vout, "never heard of chroma 0x%.8x (%4.4s)",
1386                      p_vout->output.i_chroma, (char*)&p_vout->output.i_chroma );
1387             p_pic->i_planes = 0;
1388             return -1;
1389     }
1390
1391 #endif /* !MODULE_NAME_IS_glx */
1392
1393     return 0;
1394 }
1395
1396 /*****************************************************************************
1397  * FreePicture: destroy a picture allocated with NewPicture
1398  *****************************************************************************
1399  * Destroy XImage AND associated data. If using Shm, detach shared memory
1400  * segment from server and process, then free it. The XDestroyImage manpage
1401  * says that both the image structure _and_ the data pointed to by the
1402  * image structure are freed, so no need to free p_image->data.
1403  *****************************************************************************/
1404 static void FreePicture( vout_thread_t *p_vout, picture_t *p_pic )
1405 {
1406     /* The order of operations is correct */
1407 #ifdef HAVE_SYS_SHM_H
1408     if( p_vout->p_sys->b_shm )
1409     {
1410         XShmDetach( p_vout->p_sys->p_display, &p_pic->p_sys->shminfo );
1411         IMAGE_FREE( p_pic->p_sys->p_image );
1412
1413         shmctl( p_pic->p_sys->shminfo.shmid, IPC_RMID, 0 );
1414         if( shmdt( p_pic->p_sys->shminfo.shmaddr ) )
1415         {
1416             msg_Err( p_vout, "cannot detach shared memory (%s)",
1417                              strerror(errno) );
1418         }
1419     }
1420     else
1421 #endif
1422     {
1423         IMAGE_FREE( p_pic->p_sys->p_image );
1424     }
1425
1426     /* Do NOT use XFlush here ! */
1427     XSync( p_vout->p_sys->p_display, False );
1428
1429     free( p_pic->p_sys );
1430 }
1431
1432 /*****************************************************************************
1433  * ToggleFullScreen: Enable or disable full screen mode
1434  *****************************************************************************
1435  * This function will switch between fullscreen and window mode.
1436  *****************************************************************************/
1437 static void ToggleFullScreen ( vout_thread_t *p_vout )
1438 {
1439     Atom prop;
1440     XEvent xevent;
1441     mwmhints_t mwmhints;
1442     XSetWindowAttributes attributes;
1443
1444 #ifdef HAVE_XINERAMA
1445     int i_d1, i_d2;
1446 #endif
1447
1448     p_vout->b_fullscreen = !p_vout->b_fullscreen;
1449
1450     if( p_vout->b_fullscreen )
1451     {
1452         msg_Dbg( p_vout, "entering fullscreen mode" );
1453
1454         p_vout->p_sys->b_altfullscreen =
1455             config_GetInt( p_vout, MODULE_STRING "-altfullscreen" );
1456
1457         XUnmapWindow( p_vout->p_sys->p_display,
1458                       p_vout->p_sys->p_win->base_window );
1459
1460         p_vout->p_sys->p_win = &p_vout->p_sys->fullscreen_window;
1461
1462         CreateWindow( p_vout, p_vout->p_sys->p_win );
1463         XDestroyWindow( p_vout->p_sys->p_display,
1464                         p_vout->p_sys->fullscreen_window.video_window );
1465         XReparentWindow( p_vout->p_sys->p_display,
1466                          p_vout->p_sys->original_window.video_window,
1467                          p_vout->p_sys->fullscreen_window.base_window, 0, 0 );
1468         p_vout->p_sys->fullscreen_window.video_window =
1469             p_vout->p_sys->original_window.video_window;
1470
1471         /* To my knowledge there are two ways to create a borderless window.
1472          * There's the generic way which is to tell x to bypass the window
1473          * manager, but this creates problems with the focus of other
1474          * applications.
1475          * The other way is to use the motif property "_MOTIF_WM_HINTS" which
1476          * luckily seems to be supported by most window managers. */
1477         if( !p_vout->p_sys->b_altfullscreen )
1478         {
1479             mwmhints.flags = MWM_HINTS_DECORATIONS;
1480             mwmhints.decorations = False;
1481
1482             prop = XInternAtom( p_vout->p_sys->p_display, "_MOTIF_WM_HINTS",
1483                                 False );
1484             XChangeProperty( p_vout->p_sys->p_display,
1485                              p_vout->p_sys->p_win->base_window,
1486                              prop, prop, 32, PropModeReplace,
1487                              (unsigned char *)&mwmhints,
1488                              PROP_MWM_HINTS_ELEMENTS );
1489         }
1490         else
1491         {
1492             /* brute force way to remove decorations */
1493             attributes.override_redirect = True;
1494             XChangeWindowAttributes( p_vout->p_sys->p_display,
1495                                      p_vout->p_sys->p_win->base_window,
1496                                      CWOverrideRedirect,
1497                                      &attributes);
1498
1499             /* Make sure the change is effective */
1500             XReparentWindow( p_vout->p_sys->p_display,
1501                              p_vout->p_sys->p_win->base_window,
1502                              DefaultRootWindow( p_vout->p_sys->p_display ),
1503                              0, 0 );
1504         }
1505
1506         if( p_vout->p_sys->b_net_wm_state_fullscreen )
1507         {
1508             XClientMessageEvent event;
1509
1510             memset( &event, 0, sizeof( XClientMessageEvent ) );
1511
1512             event.type = ClientMessage;
1513             event.message_type = p_vout->p_sys->net_wm_state;
1514             event.display = p_vout->p_sys->p_display;
1515             event.window = p_vout->p_sys->p_win->base_window;
1516             event.format = 32;
1517             event.data.l[ 0 ] = 1; /* set property */
1518             event.data.l[ 1 ] = p_vout->p_sys->net_wm_state_fullscreen;
1519
1520             XSendEvent( p_vout->p_sys->p_display,
1521                         DefaultRootWindow( p_vout->p_sys->p_display ),
1522                         False, SubstructureRedirectMask,
1523                         (XEvent*)&event );
1524         }
1525
1526         /* Make sure the change is effective */
1527         XReparentWindow( p_vout->p_sys->p_display,
1528                          p_vout->p_sys->p_win->base_window,
1529                          DefaultRootWindow( p_vout->p_sys->p_display ),
1530                          0, 0 );
1531
1532 #ifdef HAVE_XINERAMA
1533         if( XineramaQueryExtension( p_vout->p_sys->p_display, &i_d1, &i_d2 ) &&
1534             XineramaIsActive( p_vout->p_sys->p_display ) )
1535         {
1536             XineramaScreenInfo *screens;   /* infos for xinerama */
1537             int i_num_screens;
1538
1539             msg_Dbg( p_vout, "using XFree Xinerama extension");
1540
1541 #define SCREEN p_vout->p_sys->p_win->i_screen
1542
1543             /* Get Information about Xinerama (num of screens) */
1544             screens = XineramaQueryScreens( p_vout->p_sys->p_display,
1545                                             &i_num_screens );
1546
1547             SCREEN = config_GetInt( p_vout,
1548                                         MODULE_STRING "-xineramascreen" );
1549
1550             /* just check that user has entered a good value */
1551             if( SCREEN >= i_num_screens || SCREEN < 0 )
1552             {
1553                 msg_Dbg( p_vout, "requested screen number invalid (%d/%d)", SCREEN, i_num_screens );
1554                 SCREEN = 0;
1555             }
1556
1557             /* Get the X/Y upper left corner coordinate of the above screen */
1558             p_vout->p_sys->p_win->i_x = screens[SCREEN].x_org;
1559             p_vout->p_sys->p_win->i_y = screens[SCREEN].y_org;
1560
1561             /* Set the Height/width to the screen resolution */
1562             p_vout->p_sys->p_win->i_width = screens[SCREEN].width;
1563             p_vout->p_sys->p_win->i_height = screens[SCREEN].height;
1564
1565             XFree(screens);
1566
1567 #undef SCREEN
1568
1569         }
1570         else
1571 #endif
1572         {
1573             /* The window wasn't necessarily created at the requested size */
1574             p_vout->p_sys->p_win->i_x = p_vout->p_sys->p_win->i_y = 0;
1575
1576 #ifdef HAVE_XF86VIDMODE
1577             XF86VidModeModeLine mode;
1578             int i_dummy;
1579
1580             if( XF86VidModeGetModeLine( p_vout->p_sys->p_display,
1581                                         p_vout->p_sys->i_screen, &i_dummy,
1582                                         &mode ) )
1583             {
1584                 p_vout->p_sys->p_win->i_width = mode.hdisplay;
1585                 p_vout->p_sys->p_win->i_height = mode.vdisplay;
1586
1587                 /* move cursor to the middle of the window to prevent
1588                  * unwanted display move if the display is smaller than the
1589                  * full desktop */
1590                 XWarpPointer( p_vout->p_sys->p_display, None,
1591                               p_vout->p_sys->p_win->base_window, 0, 0, 0, 0,
1592                               mode.hdisplay / 2 , mode.vdisplay / 2 );
1593                 /* force desktop view to upper left corner */
1594                 XF86VidModeSetViewPort( p_vout->p_sys->p_display,
1595                                         p_vout->p_sys->i_screen, 0, 0 );
1596             }
1597             else
1598 #endif
1599             {
1600                 p_vout->p_sys->p_win->i_width =
1601                     DisplayWidth( p_vout->p_sys->p_display,
1602                                 p_vout->p_sys->i_screen );
1603                 p_vout->p_sys->p_win->i_height =
1604                     DisplayHeight( p_vout->p_sys->p_display,
1605                                 p_vout->p_sys->i_screen );
1606             }
1607
1608         }
1609
1610         XMoveResizeWindow( p_vout->p_sys->p_display,
1611                            p_vout->p_sys->p_win->base_window,
1612                            p_vout->p_sys->p_win->i_x,
1613                            p_vout->p_sys->p_win->i_y,
1614                            p_vout->p_sys->p_win->i_width,
1615                            p_vout->p_sys->p_win->i_height );
1616     }
1617     else
1618     {
1619         msg_Dbg( p_vout, "leaving fullscreen mode" );
1620
1621         XReparentWindow( p_vout->p_sys->p_display,
1622                          p_vout->p_sys->original_window.video_window,
1623                          p_vout->p_sys->original_window.base_window, 0, 0 );
1624
1625         p_vout->p_sys->fullscreen_window.video_window = None;
1626         DestroyWindow( p_vout, &p_vout->p_sys->fullscreen_window );
1627         p_vout->p_sys->p_win = &p_vout->p_sys->original_window;
1628
1629         XMapWindow( p_vout->p_sys->p_display,
1630                     p_vout->p_sys->p_win->base_window );
1631     }
1632
1633     /* Unfortunately, using XSync() here is not enough to ensure the
1634      * window has already been mapped because the XMapWindow() request
1635      * has not necessarily been sent directly to our window (remember,
1636      * the call is first redirected to the window manager) */
1637     do
1638     {
1639         XWindowEvent( p_vout->p_sys->p_display,
1640                       p_vout->p_sys->p_win->base_window,
1641                       StructureNotifyMask, &xevent );
1642     } while( xevent.type != MapNotify );
1643
1644     /* Be careful, this can generate a BadMatch error if the window is not
1645      * already mapped by the server (see above) */
1646     XSetInputFocus(p_vout->p_sys->p_display,
1647                    p_vout->p_sys->p_win->base_window,
1648                    RevertToParent,
1649                    CurrentTime);
1650
1651     /* signal that the size needs to be updated */
1652     p_vout->i_changes |= VOUT_SIZE_CHANGE;
1653 }
1654
1655 /*****************************************************************************
1656  * EnableXScreenSaver: enable screen saver
1657  *****************************************************************************
1658  * This function enables the screen saver on a display after it has been
1659  * disabled by XDisableScreenSaver.
1660  * FIXME: what happens if multiple vlc sessions are running at the same
1661  *        time ???
1662  *****************************************************************************/
1663 static void EnableXScreenSaver( vout_thread_t *p_vout )
1664 {
1665 #ifdef DPMSINFO_IN_DPMS_H
1666     int dummy;
1667 #endif
1668
1669     if( p_vout->p_sys->i_ss_timeout )
1670     {
1671         XSetScreenSaver( p_vout->p_sys->p_display, p_vout->p_sys->i_ss_timeout,
1672                          p_vout->p_sys->i_ss_interval,
1673                          p_vout->p_sys->i_ss_blanking,
1674                          p_vout->p_sys->i_ss_exposure );
1675     }
1676
1677     /* Restore DPMS settings */
1678 #ifdef DPMSINFO_IN_DPMS_H
1679     if( DPMSQueryExtension( p_vout->p_sys->p_display, &dummy, &dummy ) )
1680     {
1681         if( p_vout->p_sys->b_ss_dpms )
1682         {
1683             DPMSEnable( p_vout->p_sys->p_display );
1684         }
1685     }
1686 #endif
1687 }
1688
1689 /*****************************************************************************
1690  * DisableXScreenSaver: disable screen saver
1691  *****************************************************************************
1692  * See XEnableXScreenSaver
1693  *****************************************************************************/
1694 static void DisableXScreenSaver( vout_thread_t *p_vout )
1695 {
1696 #ifdef DPMSINFO_IN_DPMS_H
1697     int dummy;
1698 #endif
1699
1700     /* Save screen saver information */
1701     XGetScreenSaver( p_vout->p_sys->p_display, &p_vout->p_sys->i_ss_timeout,
1702                      &p_vout->p_sys->i_ss_interval,
1703                      &p_vout->p_sys->i_ss_blanking,
1704                      &p_vout->p_sys->i_ss_exposure );
1705
1706     /* Disable screen saver */
1707     if( p_vout->p_sys->i_ss_timeout )
1708     {
1709         XSetScreenSaver( p_vout->p_sys->p_display, 0,
1710                          p_vout->p_sys->i_ss_interval,
1711                          p_vout->p_sys->i_ss_blanking,
1712                          p_vout->p_sys->i_ss_exposure );
1713     }
1714
1715     /* Disable DPMS */
1716 #ifdef DPMSINFO_IN_DPMS_H
1717     if( DPMSQueryExtension( p_vout->p_sys->p_display, &dummy, &dummy ) )
1718     {
1719         CARD16 unused;
1720         /* Save DPMS current state */
1721         DPMSInfo( p_vout->p_sys->p_display, &unused,
1722                   &p_vout->p_sys->b_ss_dpms );
1723         DPMSDisable( p_vout->p_sys->p_display );
1724    }
1725 #endif
1726 }
1727
1728 /*****************************************************************************
1729  * CreateCursor: create a blank mouse pointer
1730  *****************************************************************************/
1731 static void CreateCursor( vout_thread_t *p_vout )
1732 {
1733     XColor cursor_color;
1734
1735     p_vout->p_sys->cursor_pixmap =
1736         XCreatePixmap( p_vout->p_sys->p_display,
1737                        DefaultRootWindow( p_vout->p_sys->p_display ),
1738                        1, 1, 1 );
1739
1740     XParseColor( p_vout->p_sys->p_display,
1741                  XCreateColormap( p_vout->p_sys->p_display,
1742                                   DefaultRootWindow(
1743                                                     p_vout->p_sys->p_display ),
1744                                   DefaultVisual(
1745                                                 p_vout->p_sys->p_display,
1746                                                 p_vout->p_sys->i_screen ),
1747                                   AllocNone ),
1748                  "black", &cursor_color );
1749
1750     p_vout->p_sys->blank_cursor =
1751         XCreatePixmapCursor( p_vout->p_sys->p_display,
1752                              p_vout->p_sys->cursor_pixmap,
1753                              p_vout->p_sys->cursor_pixmap,
1754                              &cursor_color, &cursor_color, 1, 1 );
1755 }
1756
1757 /*****************************************************************************
1758  * DestroyCursor: destroy the blank mouse pointer
1759  *****************************************************************************/
1760 static void DestroyCursor( vout_thread_t *p_vout )
1761 {
1762     XFreePixmap( p_vout->p_sys->p_display, p_vout->p_sys->cursor_pixmap );
1763 }
1764
1765 /*****************************************************************************
1766  * ToggleCursor: hide or show the mouse pointer
1767  *****************************************************************************
1768  * This function hides the X pointer if it is visible by setting the pointer
1769  * sprite to a blank one. To show it again, we disable the sprite.
1770  *****************************************************************************/
1771 static void ToggleCursor( vout_thread_t *p_vout )
1772 {
1773     if( p_vout->p_sys->b_mouse_pointer_visible )
1774     {
1775         XDefineCursor( p_vout->p_sys->p_display,
1776                        p_vout->p_sys->p_win->base_window,
1777                        p_vout->p_sys->blank_cursor );
1778         p_vout->p_sys->b_mouse_pointer_visible = 0;
1779     }
1780     else
1781     {
1782         XUndefineCursor( p_vout->p_sys->p_display,
1783                          p_vout->p_sys->p_win->base_window );
1784         p_vout->p_sys->b_mouse_pointer_visible = 1;
1785     }
1786 }
1787
1788 #ifdef MODULE_NAME_IS_xvideo
1789 /*****************************************************************************
1790  * XVideoGetPort: get YUV12 port
1791  *****************************************************************************/
1792 static int XVideoGetPort( vout_thread_t *p_vout,
1793                           vlc_fourcc_t i_chroma, vlc_fourcc_t *pi_newchroma )
1794 {
1795     XvAdaptorInfo *p_adaptor;
1796     unsigned int i;
1797     int i_adaptor, i_num_adaptors, i_requested_adaptor;
1798     int i_selected_port;
1799
1800     switch( XvQueryExtension( p_vout->p_sys->p_display, &i, &i, &i, &i, &i ) )
1801     {
1802         case Success:
1803             break;
1804
1805         case XvBadExtension:
1806             msg_Warn( p_vout, "XvBadExtension" );
1807             return -1;
1808
1809         case XvBadAlloc:
1810             msg_Warn( p_vout, "XvBadAlloc" );
1811             return -1;
1812
1813         default:
1814             msg_Warn( p_vout, "XvQueryExtension failed" );
1815             return -1;
1816     }
1817
1818     switch( XvQueryAdaptors( p_vout->p_sys->p_display,
1819                              DefaultRootWindow( p_vout->p_sys->p_display ),
1820                              &i_num_adaptors, &p_adaptor ) )
1821     {
1822         case Success:
1823             break;
1824
1825         case XvBadExtension:
1826             msg_Warn( p_vout, "XvBadExtension for XvQueryAdaptors" );
1827             return -1;
1828
1829         case XvBadAlloc:
1830             msg_Warn( p_vout, "XvBadAlloc for XvQueryAdaptors" );
1831             return -1;
1832
1833         default:
1834             msg_Warn( p_vout, "XvQueryAdaptors failed" );
1835             return -1;
1836     }
1837
1838     i_selected_port = -1;
1839     i_requested_adaptor = config_GetInt( p_vout, "xvideo-adaptor" );
1840
1841     for( i_adaptor = 0; i_adaptor < i_num_adaptors; ++i_adaptor )
1842     {
1843         XvImageFormatValues *p_formats;
1844         int i_format, i_num_formats;
1845         int i_port;
1846
1847         /* If we requested an adaptor and it's not this one, we aren't
1848          * interested */
1849         if( i_requested_adaptor != -1 && i_adaptor != i_requested_adaptor )
1850         {
1851             continue;
1852         }
1853
1854         /* If the adaptor doesn't have the required properties, skip it */
1855         if( !( p_adaptor[ i_adaptor ].type & XvInputMask ) ||
1856             !( p_adaptor[ i_adaptor ].type & XvImageMask ) )
1857         {
1858             continue;
1859         }
1860
1861         /* Check that adaptor supports our requested format... */
1862         p_formats = XvListImageFormats( p_vout->p_sys->p_display,
1863                                         p_adaptor[i_adaptor].base_id,
1864                                         &i_num_formats );
1865
1866         for( i_format = 0;
1867              i_format < i_num_formats && ( i_selected_port == -1 );
1868              i_format++ )
1869         {
1870             XvAttribute     *p_attr;
1871             int             i_attr, i_num_attributes;
1872
1873             /* If this is not the format we want, or at least a
1874              * similar one, forget it */
1875             if( !vout_ChromaCmp( p_formats[ i_format ].id, i_chroma ) )
1876             {
1877                 continue;
1878             }
1879
1880             /* Look for the first available port supporting this format */
1881             for( i_port = p_adaptor[i_adaptor].base_id;
1882                  ( i_port < (int)(p_adaptor[i_adaptor].base_id
1883                                    + p_adaptor[i_adaptor].num_ports) )
1884                    && ( i_selected_port == -1 );
1885                  i_port++ )
1886             {
1887                 if( XvGrabPort( p_vout->p_sys->p_display, i_port, CurrentTime )
1888                      == Success )
1889                 {
1890                     i_selected_port = i_port;
1891                     *pi_newchroma = p_formats[ i_format ].id;
1892                 }
1893             }
1894
1895             /* If no free port was found, forget it */
1896             if( i_selected_port == -1 )
1897             {
1898                 continue;
1899             }
1900
1901             /* If we found a port, print information about it */
1902             msg_Dbg( p_vout, "adaptor %i, port %i, format 0x%x (%4.4s) %s",
1903                      i_adaptor, i_selected_port, p_formats[ i_format ].id,
1904                      (char *)&p_formats[ i_format ].id,
1905                      ( p_formats[ i_format ].format == XvPacked ) ?
1906                          "packed" : "planar" );
1907
1908             /* Make sure XV_AUTOPAINT_COLORKEY is set */
1909             p_attr = XvQueryPortAttributes( p_vout->p_sys->p_display,
1910                                             i_selected_port,
1911                                             &i_num_attributes );
1912
1913             for( i_attr = 0; i_attr < i_num_attributes; i_attr++ )
1914             {
1915                 if( !strcmp( p_attr[i_attr].name, "XV_AUTOPAINT_COLORKEY" ) )
1916                 {
1917                     const Atom autopaint =
1918                         XInternAtom( p_vout->p_sys->p_display,
1919                                      "XV_AUTOPAINT_COLORKEY", False );
1920                     XvSetPortAttribute( p_vout->p_sys->p_display,
1921                                         i_selected_port, autopaint, 1 );
1922                     break;
1923                 }
1924             }
1925
1926             if( p_attr != NULL )
1927             {
1928                 XFree( p_attr );
1929             }
1930         }
1931
1932         if( p_formats != NULL )
1933         {
1934             XFree( p_formats );
1935         }
1936
1937     }
1938
1939     if( i_num_adaptors > 0 )
1940     {
1941         XvFreeAdaptorInfo( p_adaptor );
1942     }
1943
1944     if( i_selected_port == -1 )
1945     {
1946         int i_chroma_tmp = X112VLC_FOURCC( i_chroma );
1947         if( i_requested_adaptor == -1 )
1948         {
1949             msg_Warn( p_vout, "no free XVideo port found for format "
1950                       "0x%.8x (%4.4s)", i_chroma_tmp, (char*)&i_chroma_tmp );
1951         }
1952         else
1953         {
1954             msg_Warn( p_vout, "XVideo adaptor %i does not have a free "
1955                       "XVideo port for format 0x%.8x (%4.4s)",
1956                       i_requested_adaptor, i_chroma_tmp, (char*)&i_chroma_tmp );
1957         }
1958     }
1959
1960     return i_selected_port;
1961 }
1962
1963 /*****************************************************************************
1964  * XVideoReleasePort: release YUV12 port
1965  *****************************************************************************/
1966 static void XVideoReleasePort( vout_thread_t *p_vout, int i_port )
1967 {
1968     XvUngrabPort( p_vout->p_sys->p_display, i_port, CurrentTime );
1969 }
1970 #endif
1971
1972 /*****************************************************************************
1973  * InitDisplay: open and initialize X11 device
1974  *****************************************************************************
1975  * Create a window according to video output given size, and set other
1976  * properties according to the display properties.
1977  *****************************************************************************/
1978 static int InitDisplay( vout_thread_t *p_vout )
1979 {
1980 #ifdef MODULE_NAME_IS_x11
1981     XPixmapFormatValues *       p_formats;                 /* pixmap formats */
1982     XVisualInfo *               p_xvisual;            /* visuals information */
1983     XVisualInfo                 xvisual_template;         /* visual template */
1984     int                         i_count;                       /* array size */
1985 #endif
1986
1987 #ifdef HAVE_SYS_SHM_H
1988     p_vout->p_sys->b_shm = 0;
1989
1990     if( config_GetInt( p_vout, MODULE_STRING "-shm" ) )
1991     {
1992 #   ifdef __APPLE__
1993         /* FIXME: As of 2001-03-16, XFree4 for MacOS X does not support Xshm */
1994 #   else
1995         p_vout->p_sys->b_shm =
1996                   ( XShmQueryExtension( p_vout->p_sys->p_display ) == True );
1997 #   endif
1998
1999         if( !p_vout->p_sys->b_shm )
2000         {
2001             msg_Warn( p_vout, "XShm video extension is unavailable" );
2002         }
2003     }
2004     else
2005     {
2006         msg_Dbg( p_vout, "disabling XShm video extension" );
2007     }
2008
2009 #else
2010     msg_Warn( p_vout, "XShm video extension is unavailable" );
2011
2012 #endif
2013
2014 #ifdef MODULE_NAME_IS_xvideo
2015     /* XXX The brightness and contrast values should be read from environment
2016      * XXX variables... */
2017 #if 0
2018     XVideoSetAttribute( p_vout, "XV_BRIGHTNESS", 0.5 );
2019     XVideoSetAttribute( p_vout, "XV_CONTRAST",   0.5 );
2020 #endif
2021 #endif
2022
2023 #ifdef MODULE_NAME_IS_x11
2024     /* Initialize structure */
2025     p_vout->p_sys->i_screen = DefaultScreen( p_vout->p_sys->p_display );
2026
2027     /* Get screen depth */
2028     p_vout->p_sys->i_screen_depth = XDefaultDepth( p_vout->p_sys->p_display,
2029                                                    p_vout->p_sys->i_screen );
2030     switch( p_vout->p_sys->i_screen_depth )
2031     {
2032     case 8:
2033         /*
2034          * Screen depth is 8bpp. Use PseudoColor visual with private colormap.
2035          */
2036         xvisual_template.screen =   p_vout->p_sys->i_screen;
2037         xvisual_template.class =    DirectColor;
2038         p_xvisual = XGetVisualInfo( p_vout->p_sys->p_display,
2039                                     VisualScreenMask | VisualClassMask,
2040                                     &xvisual_template, &i_count );
2041         if( p_xvisual == NULL )
2042         {
2043             msg_Err( p_vout, "no PseudoColor visual available" );
2044             return VLC_EGENERIC;
2045         }
2046         p_vout->p_sys->i_bytes_per_pixel = 1;
2047         p_vout->output.pf_setpalette = SetPalette;
2048         break;
2049     case 15:
2050     case 16:
2051     case 24:
2052     default:
2053         /*
2054          * Screen depth is higher than 8bpp. TrueColor visual is used.
2055          */
2056         xvisual_template.screen =   p_vout->p_sys->i_screen;
2057         xvisual_template.class =    TrueColor;
2058         p_xvisual = XGetVisualInfo( p_vout->p_sys->p_display,
2059                                     VisualScreenMask | VisualClassMask,
2060                                     &xvisual_template, &i_count );
2061         if( p_xvisual == NULL )
2062         {
2063             msg_Err( p_vout, "no TrueColor visual available" );
2064             return VLC_EGENERIC;
2065         }
2066
2067         p_vout->output.i_rmask = p_xvisual->red_mask;
2068         p_vout->output.i_gmask = p_xvisual->green_mask;
2069         p_vout->output.i_bmask = p_xvisual->blue_mask;
2070
2071         /* There is no difference yet between 3 and 4 Bpp. The only way
2072          * to find the actual number of bytes per pixel is to list supported
2073          * pixmap formats. */
2074         p_formats = XListPixmapFormats( p_vout->p_sys->p_display, &i_count );
2075         p_vout->p_sys->i_bytes_per_pixel = 0;
2076
2077         for( ; i_count-- ; p_formats++ )
2078         {
2079             /* Under XFree4.0, the list contains pixmap formats available
2080              * through all video depths ; so we have to check against current
2081              * depth. */
2082             if( p_formats->depth == (int)p_vout->p_sys->i_screen_depth )
2083             {
2084                 if( p_formats->bits_per_pixel / 8
2085                         > (int)p_vout->p_sys->i_bytes_per_pixel )
2086                 {
2087                     p_vout->p_sys->i_bytes_per_pixel =
2088                                                p_formats->bits_per_pixel / 8;
2089                 }
2090             }
2091         }
2092         break;
2093     }
2094     p_vout->p_sys->p_visual = p_xvisual->visual;
2095     XFree( p_xvisual );
2096 #endif
2097
2098     return VLC_SUCCESS;
2099 }
2100
2101 #ifdef HAVE_SYS_SHM_H
2102 /*****************************************************************************
2103  * CreateShmImage: create an XImage or XvImage using shared memory extension
2104  *****************************************************************************
2105  * Prepare an XImage or XvImage for display function.
2106  * The order of the operations respects the recommandations of the mit-shm
2107  * document by J.Corbet and K.Packard. Most of the parameters were copied from
2108  * there. See http://ftp.xfree86.org/pub/XFree86/4.0/doc/mit-shm.TXT
2109  *****************************************************************************/
2110 static IMAGE_TYPE * CreateShmImage( vout_thread_t *p_vout,
2111                                     Display* p_display, EXTRA_ARGS_SHM,
2112                                     int i_width, int i_height )
2113 {
2114     IMAGE_TYPE *p_image;
2115     Status result;
2116
2117     /* Create XImage / XvImage */
2118 #ifdef MODULE_NAME_IS_xvideo
2119
2120     /* Make sure the buffer is aligned to multiple of 16 */
2121     i_height = ( i_height + 15 ) >> 4 << 4;
2122     i_width = ( i_width + 15 ) >> 4 << 4;
2123
2124     p_image = XvShmCreateImage( p_display, i_xvport, i_chroma, 0,
2125                                 i_width, i_height, p_shm );
2126 #else
2127     p_image = XShmCreateImage( p_display, p_visual, i_depth, ZPixmap, 0,
2128                                p_shm, i_width, i_height );
2129 #endif
2130     if( p_image == NULL )
2131     {
2132         msg_Err( p_vout, "image creation failed" );
2133         return NULL;
2134     }
2135
2136     /* Allocate shared memory segment - 0776 set the access permission
2137      * rights (like umask), they are not yet supported by all X servers */
2138     p_shm->shmid = shmget( IPC_PRIVATE, DATA_SIZE(p_image), IPC_CREAT | 0776 );
2139     if( p_shm->shmid < 0 )
2140     {
2141         msg_Err( p_vout, "cannot allocate shared image data (%s)",
2142                          strerror( errno ) );
2143         IMAGE_FREE( p_image );
2144         return NULL;
2145     }
2146
2147     /* Attach shared memory segment to process (read/write) */
2148     p_shm->shmaddr = p_image->data = shmat( p_shm->shmid, 0, 0 );
2149     if(! p_shm->shmaddr )
2150     {
2151         msg_Err( p_vout, "cannot attach shared memory (%s)",
2152                          strerror(errno));
2153         IMAGE_FREE( p_image );
2154         shmctl( p_shm->shmid, IPC_RMID, 0 );
2155         return NULL;
2156     }
2157
2158     /* Read-only data. We won't be using XShmGetImage */
2159     p_shm->readOnly = True;
2160
2161     /* Attach shared memory segment to X server */
2162     XSynchronize( p_display, True );
2163     b_shm = VLC_TRUE;
2164     result = XShmAttach( p_display, p_shm );
2165     if( result == False || !b_shm )
2166     {
2167         msg_Err( p_vout, "cannot attach shared memory to X server" );
2168         IMAGE_FREE( p_image );
2169         shmctl( p_shm->shmid, IPC_RMID, 0 );
2170         shmdt( p_shm->shmaddr );
2171         return NULL;
2172     }
2173     XSynchronize( p_display, False );
2174
2175     /* Send image to X server. This instruction is required, since having
2176      * built a Shm XImage and not using it causes an error on XCloseDisplay,
2177      * and remember NOT to use XFlush ! */
2178     XSync( p_display, False );
2179
2180 #if 0
2181     /* Mark the shm segment to be removed when there are no more
2182      * attachements, so it is automatic on process exit or after shmdt */
2183     shmctl( p_shm->shmid, IPC_RMID, 0 );
2184 #endif
2185
2186     return p_image;
2187 }
2188 #endif
2189
2190 /*****************************************************************************
2191  * CreateImage: create an XImage or XvImage
2192  *****************************************************************************
2193  * Create a simple image used as a buffer.
2194  *****************************************************************************/
2195 static IMAGE_TYPE * CreateImage( vout_thread_t *p_vout,
2196                                  Display *p_display, EXTRA_ARGS,
2197                                  int i_width, int i_height )
2198 {
2199     byte_t *    p_data;                           /* image data storage zone */
2200     IMAGE_TYPE *p_image;
2201 #ifdef MODULE_NAME_IS_x11
2202     int         i_quantum;                     /* XImage quantum (see below) */
2203     int         i_bytes_per_line;
2204 #endif
2205
2206     /* Allocate memory for image */
2207 #ifdef MODULE_NAME_IS_xvideo
2208
2209     /* Make sure the buffer is aligned to multiple of 16 */
2210     i_height = ( i_height + 15 ) >> 4 << 4;
2211     i_width = ( i_width + 15 ) >> 4 << 4;
2212
2213     p_data = (byte_t *) malloc( i_width * i_height * i_bits_per_pixel / 8 );
2214 #elif defined(MODULE_NAME_IS_x11)
2215     i_bytes_per_line = i_width * i_bytes_per_pixel;
2216     p_data = (byte_t *) malloc( i_bytes_per_line * i_height );
2217 #endif
2218     if( !p_data )
2219     {
2220         msg_Err( p_vout, "out of memory" );
2221         return NULL;
2222     }
2223
2224 #ifdef MODULE_NAME_IS_x11
2225     /* Optimize the quantum of a scanline regarding its size - the quantum is
2226        a diviser of the number of bits between the start of two scanlines. */
2227     if( i_bytes_per_line & 0xf )
2228     {
2229         i_quantum = 0x8;
2230     }
2231     else if( i_bytes_per_line & 0x10 )
2232     {
2233         i_quantum = 0x10;
2234     }
2235     else
2236     {
2237         i_quantum = 0x20;
2238     }
2239 #endif
2240
2241     /* Create XImage. p_data will be automatically freed */
2242 #ifdef MODULE_NAME_IS_xvideo
2243     p_image = XvCreateImage( p_display, i_xvport, i_chroma,
2244                              p_data, i_width, i_height );
2245 #elif defined(MODULE_NAME_IS_x11)
2246     p_image = XCreateImage( p_display, p_visual, i_depth, ZPixmap, 0,
2247                             p_data, i_width, i_height, i_quantum, 0 );
2248 #endif
2249     if( p_image == NULL )
2250     {
2251         msg_Err( p_vout, "XCreateImage() failed" );
2252         free( p_data );
2253         return NULL;
2254     }
2255
2256     return p_image;
2257 }
2258
2259 /*****************************************************************************
2260  * X11ErrorHandler: replace error handler so we can intercept some of them
2261  *****************************************************************************/
2262 static int X11ErrorHandler( Display * display, XErrorEvent * event )
2263 {
2264     /* Ingnore errors on XSetInputFocus()
2265      * (they happen when a window is not yet mapped) */
2266     if( event->request_code == X_SetInputFocus )
2267     {
2268         fprintf(stderr, "XSetInputFocus failed\n");
2269         return 0;
2270     }
2271
2272     if( event->request_code == 150 /* MIT-SHM */ &&
2273         event->minor_code == X_ShmAttach )
2274     {
2275         fprintf(stderr, "XShmAttach failed\n");
2276         b_shm = VLC_FALSE;
2277         return 0;
2278     }
2279
2280     XSetErrorHandler(NULL);
2281     return (XSetErrorHandler(X11ErrorHandler))( display, event );
2282 }
2283
2284 #ifdef MODULE_NAME_IS_x11
2285 /*****************************************************************************
2286  * SetPalette: sets an 8 bpp palette
2287  *****************************************************************************
2288  * This function sets the palette given as an argument. It does not return
2289  * anything, but could later send information on which colors it was unable
2290  * to set.
2291  *****************************************************************************/
2292 static void SetPalette( vout_thread_t *p_vout,
2293                         uint16_t *red, uint16_t *green, uint16_t *blue )
2294 {
2295     int i;
2296     XColor p_colors[255];
2297
2298     /* allocate palette */
2299     for( i = 0; i < 255; i++ )
2300     {
2301         /* kludge: colors are indexed reversely because color 255 seems
2302          * to be reserved for black even if we try to set it to white */
2303         p_colors[ i ].pixel = 255 - i;
2304         p_colors[ i ].pad   = 0;
2305         p_colors[ i ].flags = DoRed | DoGreen | DoBlue;
2306         p_colors[ i ].red   = red[ 255 - i ];
2307         p_colors[ i ].blue  = blue[ 255 - i ];
2308         p_colors[ i ].green = green[ 255 - i ];
2309     }
2310
2311     XStoreColors( p_vout->p_sys->p_display,
2312                   p_vout->p_sys->colormap, p_colors, 255 );
2313 }
2314 #endif
2315
2316 /*****************************************************************************
2317  * Control: control facility for the vout
2318  *****************************************************************************/
2319 static int Control( vout_thread_t *p_vout, int i_query, va_list args )
2320 {
2321     vlc_bool_t b_arg;
2322     unsigned int i_width, i_height;
2323     unsigned int *pi_width, *pi_height;
2324
2325     switch( i_query )
2326     {
2327         case VOUT_GET_SIZE:
2328             if( p_vout->p_sys->p_win->owner_window )
2329                 return vout_ControlWindow( p_vout,
2330                     (void *)p_vout->p_sys->p_win->owner_window, i_query, args);
2331
2332             pi_width  = va_arg( args, unsigned int * );
2333             pi_height = va_arg( args, unsigned int * );
2334
2335             vlc_mutex_lock( &p_vout->p_sys->lock );
2336             *pi_width  = p_vout->p_sys->p_win->i_width;
2337             *pi_height = p_vout->p_sys->p_win->i_height;
2338             vlc_mutex_unlock( &p_vout->p_sys->lock );
2339             return VLC_SUCCESS;
2340
2341         case VOUT_SET_SIZE:
2342             if( p_vout->p_sys->p_win->owner_window )
2343                 return vout_ControlWindow( p_vout,
2344                     (void *)p_vout->p_sys->p_win->owner_window, i_query, args);
2345
2346             vlc_mutex_lock( &p_vout->p_sys->lock );
2347
2348             i_width  = va_arg( args, unsigned int );
2349             i_height = va_arg( args, unsigned int );
2350             if( !i_width ) i_width = p_vout->i_window_width;
2351             if( !i_height ) i_height = p_vout->i_window_height;
2352
2353             /* Update dimensions */
2354             XResizeWindow( p_vout->p_sys->p_display,
2355                            p_vout->p_sys->p_win->base_window,
2356                            i_width, i_height );
2357
2358             vlc_mutex_unlock( &p_vout->p_sys->lock );
2359             return VLC_SUCCESS;
2360
2361        case VOUT_CLOSE:
2362             vlc_mutex_lock( &p_vout->p_sys->lock );
2363             XUnmapWindow( p_vout->p_sys->p_display,
2364                           p_vout->p_sys->original_window.base_window );
2365             vlc_mutex_unlock( &p_vout->p_sys->lock );
2366             /* Fall through */
2367
2368        case VOUT_REPARENT:
2369             vlc_mutex_lock( &p_vout->p_sys->lock );
2370             XReparentWindow( p_vout->p_sys->p_display,
2371                              p_vout->p_sys->original_window.base_window,
2372                              DefaultRootWindow( p_vout->p_sys->p_display ),
2373                              0, 0 );
2374             XSync( p_vout->p_sys->p_display, False );
2375             p_vout->p_sys->original_window.owner_window = 0;
2376             vlc_mutex_unlock( &p_vout->p_sys->lock );
2377             return vout_vaControlDefault( p_vout, i_query, args );
2378
2379         case VOUT_SET_STAY_ON_TOP:
2380             if( p_vout->p_sys->p_win->owner_window )
2381                 return vout_ControlWindow( p_vout,
2382                     (void *)p_vout->p_sys->p_win->owner_window, i_query, args);
2383
2384             b_arg = va_arg( args, vlc_bool_t );
2385             vlc_mutex_lock( &p_vout->p_sys->lock );
2386             WindowOnTop( p_vout, b_arg );
2387             vlc_mutex_unlock( &p_vout->p_sys->lock );
2388             return VLC_SUCCESS;
2389
2390        default:
2391             return vout_vaControlDefault( p_vout, i_query, args );
2392     }
2393 }
2394
2395 /*****************************************************************************
2396  * TestNetWMSupport: tests for Extended Window Manager Hints support
2397  *****************************************************************************/
2398 static void TestNetWMSupport( vout_thread_t *p_vout )
2399 {
2400     int i_ret, i_format;
2401     unsigned long i, i_items, i_bytesafter;
2402     Atom net_wm_supported;
2403     union { Atom *p_atom; unsigned char *p_char; } p_args;
2404
2405     p_args.p_atom = NULL;
2406
2407     p_vout->p_sys->b_net_wm_state_fullscreen = VLC_FALSE;
2408     p_vout->p_sys->b_net_wm_state_above = VLC_FALSE;
2409     p_vout->p_sys->b_net_wm_state_below = VLC_FALSE;
2410     p_vout->p_sys->b_net_wm_state_stays_on_top = VLC_FALSE;
2411
2412     net_wm_supported =
2413         XInternAtom( p_vout->p_sys->p_display, "_NET_SUPPORTED", False );
2414
2415     i_ret = XGetWindowProperty( p_vout->p_sys->p_display,
2416                                 DefaultRootWindow( p_vout->p_sys->p_display ),
2417                                 net_wm_supported,
2418                                 0, 16384, False, AnyPropertyType,
2419                                 &net_wm_supported,
2420                                 &i_format, &i_items, &i_bytesafter,
2421                                 (unsigned char **)&p_args );
2422
2423     if( i_ret != Success || i_items == 0 ) return;
2424
2425     msg_Dbg( p_vout, "Window manager supports NetWM" );
2426
2427     p_vout->p_sys->net_wm_state =
2428         XInternAtom( p_vout->p_sys->p_display, "_NET_WM_STATE", False );
2429     p_vout->p_sys->net_wm_state_fullscreen =
2430         XInternAtom( p_vout->p_sys->p_display, "_NET_WM_STATE_FULLSCREEN",
2431                      False );
2432     p_vout->p_sys->net_wm_state_above =
2433         XInternAtom( p_vout->p_sys->p_display, "_NET_WM_STATE_ABOVE", False );
2434     p_vout->p_sys->net_wm_state_below =
2435         XInternAtom( p_vout->p_sys->p_display, "_NET_WM_STATE_BELOW", False );
2436     p_vout->p_sys->net_wm_state_stays_on_top =
2437         XInternAtom( p_vout->p_sys->p_display, "_NET_WM_STATE_STAYS_ON_TOP",
2438                      False );
2439
2440     for( i = 0; i < i_items; i++ )
2441     {
2442         if( p_args.p_atom[i] == p_vout->p_sys->net_wm_state_fullscreen )
2443         {
2444             msg_Dbg( p_vout,
2445                      "Window manager supports _NET_WM_STATE_FULLSCREEN" );
2446             p_vout->p_sys->b_net_wm_state_fullscreen = VLC_TRUE;
2447         }
2448         else if( p_args.p_atom[i] == p_vout->p_sys->net_wm_state_above )
2449         {
2450             msg_Dbg( p_vout, "Window manager supports _NET_WM_STATE_ABOVE" );
2451             p_vout->p_sys->b_net_wm_state_above = VLC_TRUE;
2452         }
2453         else if( p_args.p_atom[i] == p_vout->p_sys->net_wm_state_below )
2454         {
2455             msg_Dbg( p_vout, "Window manager supports _NET_WM_STATE_BELOW" );
2456             p_vout->p_sys->b_net_wm_state_below = VLC_TRUE;
2457         }
2458         else if( p_args.p_atom[i] == p_vout->p_sys->net_wm_state_stays_on_top )
2459         {
2460             msg_Dbg( p_vout,
2461                      "Window manager supports _NET_WM_STATE_STAYS_ON_TOP" );
2462             p_vout->p_sys->b_net_wm_state_stays_on_top = VLC_TRUE;
2463         }
2464     }
2465
2466     XFree( p_args.p_atom );
2467 }
2468
2469 /*****************************************************************************
2470  * Key events handling
2471  *****************************************************************************/
2472 static struct
2473 {
2474     int i_x11key;
2475     int i_vlckey;
2476
2477 } x11keys_to_vlckeys[] =
2478 {
2479     { XK_F1, KEY_F1 }, { XK_F2, KEY_F2 }, { XK_F3, KEY_F3 }, { XK_F4, KEY_F4 },
2480     { XK_F5, KEY_F5 }, { XK_F6, KEY_F6 }, { XK_F7, KEY_F7 }, { XK_F8, KEY_F8 },
2481     { XK_F9, KEY_F9 }, { XK_F10, KEY_F10 }, { XK_F11, KEY_F11 },
2482     { XK_F12, KEY_F12 },
2483
2484     { XK_Return, KEY_ENTER },
2485     { XK_KP_Enter, KEY_ENTER },
2486     { XK_space, KEY_SPACE },
2487     { XK_Escape, KEY_ESC },
2488
2489     { XK_Menu, KEY_MENU },
2490     { XK_Left, KEY_LEFT },
2491     { XK_Right, KEY_RIGHT },
2492     { XK_Up, KEY_UP },
2493     { XK_Down, KEY_DOWN },
2494
2495     { XK_Home, KEY_HOME },
2496     { XK_End, KEY_END },
2497     { XK_Page_Up, KEY_PAGEUP },
2498     { XK_Page_Down, KEY_PAGEDOWN },
2499
2500     { XK_Insert, KEY_INSERT },
2501     { XK_Delete, KEY_DELETE },
2502
2503     { 0, 0 }
2504 };
2505
2506 static int ConvertKey( int i_key )
2507 {
2508     int i;
2509
2510     for( i = 0; x11keys_to_vlckeys[i].i_x11key != 0; i++ )
2511     {
2512         if( x11keys_to_vlckeys[i].i_x11key == i_key )
2513         {
2514             return x11keys_to_vlckeys[i].i_vlckey;
2515         }
2516     }
2517
2518     return 0;
2519 }
2520
2521 /*****************************************************************************
2522  * WindowOnTop: Switches the "always on top" state of the video window.
2523  *****************************************************************************/
2524 static int WindowOnTop( vout_thread_t *p_vout, vlc_bool_t b_on_top )
2525 {
2526     if( p_vout->p_sys->b_net_wm_state_stays_on_top )
2527     {
2528         XClientMessageEvent event;
2529
2530         memset( &event, 0, sizeof( XClientMessageEvent ) );
2531
2532         event.type = ClientMessage;
2533         event.message_type = p_vout->p_sys->net_wm_state;
2534         event.display = p_vout->p_sys->p_display;
2535         event.window = p_vout->p_sys->p_win->base_window;
2536         event.format = 32;
2537         event.data.l[ 0 ] = b_on_top; /* set property */
2538         event.data.l[ 1 ] = p_vout->p_sys->net_wm_state_stays_on_top;
2539
2540         XSendEvent( p_vout->p_sys->p_display,
2541                     DefaultRootWindow( p_vout->p_sys->p_display ),
2542                     False, SubstructureRedirectMask,
2543                     (XEvent*)&event );
2544     }
2545
2546     return VLC_SUCCESS;
2547 }