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