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