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