]> git.sesse.net Git - vlc/blob - plugins/x11/vout_x11.c
* removed useless includes in intf_gnome.c
[vlc] / plugins / x11 / vout_x11.c
1 /*****************************************************************************
2  * vout_x11.c: X11 video output display method
3  *****************************************************************************
4  * Copyright (C) 1998, 1999, 2000 VideoLAN
5  * $Id: vout_x11.c,v 1.12 2001/02/15 07:59:38 sam Exp $
6  *
7  * Authors: Vincent Seguin <seguin@via.ecp.fr>
8  *          Samuel Hocevar <sam@zoy.org>
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  * 
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
23  *****************************************************************************/
24
25 /*****************************************************************************
26  * Preamble
27  *****************************************************************************/
28 #include "defs.h"
29
30 #include <errno.h>                                                 /* ENOMEM */
31 #include <stdlib.h>                                                /* free() */
32 #include <string.h>                                            /* strerror() */
33
34 #ifdef HAVE_MACHINE_PARAM_H
35 /* BSD */
36 #include <machine/param.h>
37 #include <sys/types.h>                                     /* typedef ushort */
38 #include <sys/ipc.h>
39 #endif
40
41 #include <sys/shm.h>                                   /* shmget(), shmctl() */
42 #include <X11/Xlib.h>
43 #include <X11/Xutil.h>
44 #include <X11/keysym.h>
45 #include <X11/extensions/XShm.h>
46
47 #include "config.h"
48 #include "common.h"
49 #include "threads.h"
50 #include "mtime.h"
51 #include "tests.h"
52 #include "modules.h"
53
54 #include "video.h"
55 #include "video_output.h"
56
57 #include "interface.h"
58 #include "intf_msg.h"
59
60 #include "main.h"
61
62 /*****************************************************************************
63  * vout_sys_t: video output X11 method descriptor
64  *****************************************************************************
65  * This structure is part of the video output thread descriptor.
66  * It describes the X11 specific properties of an output thread. X11 video
67  * output is performed through regular resizable windows. Windows can be
68  * dynamically resized to adapt to the size of the streams.
69  *****************************************************************************/
70 typedef struct vout_sys_s
71 {
72     /* User settings */
73     boolean_t           b_shm;               /* shared memory extension flag */
74
75     /* Internal settings and properties */
76     Display *           p_display;                        /* display pointer */
77     Visual *            p_visual;                          /* visual pointer */
78     int                 i_screen;                           /* screen number */
79     Window              window;                               /* root window */
80     GC                  gc;              /* graphic context instance handler */
81     Colormap            colormap;               /* colormap used (8bpp only) */
82
83     /* Display buffers and shared memory information */
84     XImage *            p_ximage[2];                       /* XImage pointer */
85     XShmSegmentInfo     shm_info[2];       /* shared memory zone information */
86
87     /* X11 generic properties */
88     Atom                wm_protocols;
89     Atom                wm_delete_window;
90
91     int                 i_width;                     /* width of main window */
92     int                 i_height;                   /* height of main window */
93
94     /* Screen saver properties */
95     int                 i_ss_timeout;                             /* timeout */
96     int                 i_ss_interval;           /* interval between changes */
97     int                 i_ss_blanking;                      /* blanking mode */
98     int                 i_ss_exposure;                      /* exposure mode */
99
100     /* Mouse pointer properties */
101     boolean_t           b_mouse;         /* is the mouse pointer displayed ? */
102
103 } vout_sys_t;
104
105 /*****************************************************************************
106  * Local prototypes
107  *****************************************************************************/
108 static int  vout_Probe     ( probedata_t *p_data );
109 static int  vout_Create    ( struct vout_thread_s * );
110 static int  vout_Init      ( struct vout_thread_s * );
111 static void vout_End       ( struct vout_thread_s * );
112 static void vout_Destroy   ( struct vout_thread_s * );
113 static int  vout_Manage    ( struct vout_thread_s * );
114 static void vout_Display   ( struct vout_thread_s * );
115 static void vout_SetPalette( struct vout_thread_s *, u16*, u16*, u16*, u16* );
116
117 static int  X11InitDisplay      ( vout_thread_t *p_vout, char *psz_display );
118 static int  X11CreateImage      ( vout_thread_t *p_vout, XImage **pp_ximage );
119 static void X11DestroyImage     ( XImage *p_ximage );
120 static int  X11CreateShmImage   ( vout_thread_t *p_vout, XImage **pp_ximage,
121                                   XShmSegmentInfo *p_shm_info );
122 static void X11DestroyShmImage  ( vout_thread_t *p_vout, XImage *p_ximage,
123                                   XShmSegmentInfo *p_shm_info );
124
125 static int  X11CreateWindow             ( vout_thread_t *p_vout );
126
127 /* local prototypes */
128 static void X11TogglePointer            ( vout_thread_t *p_vout );
129 static void X11EnableScreenSaver        ( vout_thread_t *p_vout );
130 static void X11DisableScreenSaver       ( vout_thread_t *p_vout );
131
132 /*****************************************************************************
133  * Functions exported as capabilities. They are declared as static so that
134  * we don't pollute the namespace too much.
135  *****************************************************************************/
136 void vout_getfunctions( function_list_t * p_function_list )
137 {
138     p_function_list->pf_probe = vout_Probe;
139     p_function_list->functions.vout.pf_create     = vout_Create;
140     p_function_list->functions.vout.pf_init       = vout_Init;
141     p_function_list->functions.vout.pf_end        = vout_End;
142     p_function_list->functions.vout.pf_destroy    = vout_Destroy;
143     p_function_list->functions.vout.pf_manage     = vout_Manage;
144     p_function_list->functions.vout.pf_display    = vout_Display;
145     p_function_list->functions.vout.pf_setpalette = vout_SetPalette;
146 }
147
148 /*****************************************************************************
149  * vout_Probe: probe the video driver and return a score
150  *****************************************************************************
151  * This function tries to initialize SDL and returns a score to the
152  * plugin manager so that it can select the best plugin.
153  *****************************************************************************/
154 static int vout_Probe( probedata_t *p_data )
155 {
156     if( TestMethod( VOUT_METHOD_VAR, "x11" ) )
157     {
158         return( 999 );
159     }
160
161     return( 50 );
162 }
163
164 /*****************************************************************************
165  * vout_Create: allocate X11 video thread output method
166  *****************************************************************************
167  * This function allocate and initialize a X11 vout method. It uses some of the
168  * vout properties to choose the window size, and change them according to the
169  * actual properties of the display.
170  *****************************************************************************/
171 static int vout_Create( vout_thread_t *p_vout )
172 {
173     char *psz_display;
174
175     /* Allocate structure */
176     p_vout->p_sys = malloc( sizeof( vout_sys_t ) );
177     if( p_vout->p_sys == NULL )
178     {
179         intf_ErrMsg("error: %s", strerror(ENOMEM) );
180         return( 1 );
181     }
182
183     /* Open display, unsing 'vlc_display' or DISPLAY environment variable */
184     psz_display = XDisplayName( main_GetPszVariable( VOUT_DISPLAY_VAR, NULL ) );
185     p_vout->p_sys->p_display = XOpenDisplay( psz_display );
186
187     if( p_vout->p_sys->p_display == NULL )                          /* error */
188     {
189         intf_ErrMsg("error: can't open display %s\n", psz_display );
190         free( p_vout->p_sys );
191         return( 1 );
192     }
193     p_vout->p_sys->i_screen = DefaultScreen( p_vout->p_sys->p_display );
194
195     /* Spawn base window - this window will include the video output window,
196      * but also command buttons, subtitles and other indicators */
197     if( X11CreateWindow( p_vout ) )
198     {
199         intf_ErrMsg("error: can't create interface window\n" );
200         XCloseDisplay( p_vout->p_sys->p_display );
201         free( p_vout->p_sys );
202         return( 1 );
203     }
204
205     /* Open and initialize device. This function issues its own error messages.
206      * Since XLib is usually not thread-safe, we can't use the same display
207      * pointer than the interface or another thread. However, the root window
208      * id is still valid. */
209     if( X11InitDisplay( p_vout, psz_display ) )
210     {
211         intf_ErrMsg("error: can't initialize X11 display" );
212         XCloseDisplay( p_vout->p_sys->p_display );
213         free( p_vout->p_sys );
214         return( 1 );
215     }
216
217     p_vout->p_sys->b_mouse = 1;
218
219     /* Disable screen saver and return */
220     X11DisableScreenSaver( p_vout );
221
222     return( 0 );
223 }
224
225 /*****************************************************************************
226  * vout_Init: initialize X11 video thread output method
227  *****************************************************************************
228  * This function create the XImages needed by the output thread. It is called
229  * at the beginning of the thread, but also each time the window is resized.
230  *****************************************************************************/
231 static int vout_Init( vout_thread_t *p_vout )
232 {
233     int i_err;
234
235     /* Create XImages using XShm extension - on failure, fall back to regular
236      * way (and destroy the first image if it was created successfully) */
237     if( p_vout->p_sys->b_shm )
238     {
239         /* Create first image */
240         i_err = X11CreateShmImage( p_vout, &p_vout->p_sys->p_ximage[0],
241                                    &p_vout->p_sys->shm_info[0] );
242         if( !i_err )                         /* first image has been created */
243         {
244             /* Create second image */
245             if( X11CreateShmImage( p_vout, &p_vout->p_sys->p_ximage[1],
246                                    &p_vout->p_sys->shm_info[1] ) )
247             {                             /* error creating the second image */
248                 X11DestroyShmImage( p_vout, p_vout->p_sys->p_ximage[0],
249                                     &p_vout->p_sys->shm_info[0] );
250                 i_err = 1;
251             }
252         }
253         if( i_err )                                      /* an error occured */
254         {
255             intf_Msg("vout: XShm video extension unavailable" );
256             p_vout->p_sys->b_shm = 0;
257         }
258     }
259
260     /* Create XImages without XShm extension */
261     if( !p_vout->p_sys->b_shm )
262     {
263         if( X11CreateImage( p_vout, &p_vout->p_sys->p_ximage[0] ) )
264         {
265             intf_ErrMsg("error: can't create images");
266             p_vout->p_sys->p_ximage[0] = NULL;
267             p_vout->p_sys->p_ximage[1] = NULL;
268             return( 1 );
269         }
270         if( X11CreateImage( p_vout, &p_vout->p_sys->p_ximage[1] ) )
271         {
272             intf_ErrMsg("error: can't create images");
273             X11DestroyImage( p_vout->p_sys->p_ximage[0] );
274             p_vout->p_sys->p_ximage[0] = NULL;
275             p_vout->p_sys->p_ximage[1] = NULL;
276             return( 1 );
277         }
278     }
279
280     /* Set bytes per line and initialize buffers */
281     p_vout->i_bytes_per_line = p_vout->p_sys->p_ximage[0]->bytes_per_line;
282     vout_SetBuffers( p_vout, p_vout->p_sys->p_ximage[ 0 ]->data,
283                      p_vout->p_sys->p_ximage[ 1 ]->data );
284     return( 0 );
285 }
286
287 /*****************************************************************************
288  * vout_End: terminate X11 video thread output method
289  *****************************************************************************
290  * Destroy the X11 XImages created by vout_Init. It is called at the end of
291  * the thread, but also each time the window is resized.
292  *****************************************************************************/
293 static void vout_End( vout_thread_t *p_vout )
294 {
295     if( p_vout->p_sys->b_shm )                             /* Shm XImages... */
296     {
297         X11DestroyShmImage( p_vout, p_vout->p_sys->p_ximage[0],
298                             &p_vout->p_sys->shm_info[0] );
299         X11DestroyShmImage( p_vout, p_vout->p_sys->p_ximage[1],
300                             &p_vout->p_sys->shm_info[1] );
301     }
302     else                                          /* ...or regular XImages */
303     {
304         X11DestroyImage( p_vout->p_sys->p_ximage[0] );
305         X11DestroyImage( p_vout->p_sys->p_ximage[1] );
306     }
307 }
308
309 /*****************************************************************************
310  * vout_Destroy: destroy X11 video thread output method
311  *****************************************************************************
312  * Terminate an output method created by vout_CreateOutputMethod
313  *****************************************************************************/
314 static void vout_Destroy( vout_thread_t *p_vout )
315 {
316     /* Enable screen saver */
317     X11EnableScreenSaver( p_vout );
318
319     /* Destroy colormap */
320     if( p_vout->i_screen_depth == 8 )
321     {
322         XFreeColormap( p_vout->p_sys->p_display, p_vout->p_sys->colormap );
323     }
324     
325     /* Destroy window */
326     XUnmapWindow( p_vout->p_sys->p_display, p_vout->p_sys->window );
327     XFreeGC( p_vout->p_sys->p_display, p_vout->p_sys->gc );
328     XDestroyWindow( p_vout->p_sys->p_display, p_vout->p_sys->window );
329
330     XCloseDisplay( p_vout->p_sys->p_display );
331
332     /* Destroy structure */
333     free( p_vout->p_sys );
334 }
335
336 /*****************************************************************************
337  * vout_Manage: handle X11 events
338  *****************************************************************************
339  * This function should be called regularly by video output thread. It manages
340  * X11 events and allows window resizing. It returns a non null value on
341  * error.
342  *****************************************************************************/
343 static int vout_Manage( vout_thread_t *p_vout )
344 {
345     XEvent      xevent;                                         /* X11 event */
346     boolean_t   b_resized;                        /* window has been resized */
347     char        i_key;                                    /* ISO Latin-1 key */
348
349     /* Handle X11 events: ConfigureNotify events are parsed to know if the
350      * output window's size changed, MapNotify and UnmapNotify to know if the
351      * window is mapped (and if the display is useful), and ClientMessages
352      * to intercept window destruction requests */
353     b_resized = 0;
354     while( XCheckWindowEvent( p_vout->p_sys->p_display, p_vout->p_sys->window,
355                               StructureNotifyMask | KeyPressMask |
356                               ButtonPressMask, &xevent ) == True )
357     {
358         /* ConfigureNotify event: prepare  */
359         if( (xevent.type == ConfigureNotify)
360             && ((xevent.xconfigure.width != p_vout->p_sys->i_width)
361                 || (xevent.xconfigure.height != p_vout->p_sys->i_height)) )
362         {
363             /* Update dimensions */
364             b_resized = 1;
365             p_vout->p_sys->i_width = xevent.xconfigure.width;
366             p_vout->p_sys->i_height = xevent.xconfigure.height;
367         }
368         /* MapNotify event: change window status and disable screen saver */
369         else if( xevent.type == MapNotify)
370         {
371             if( (p_vout != NULL) && !p_vout->b_active )
372             {
373                 X11DisableScreenSaver( p_vout );
374                 p_vout->b_active = 1;
375             }
376         }
377         /* UnmapNotify event: change window status and enable screen saver */
378         else if( xevent.type == UnmapNotify )
379         {
380             if( (p_vout != NULL) && p_vout->b_active )
381             {
382                 X11EnableScreenSaver( p_vout );
383                 p_vout->b_active = 0;
384             }
385         }
386         /* Keyboard event */
387         else if( xevent.type == KeyPress )
388         {
389             if( XLookupString( &xevent.xkey, &i_key, 1, NULL, NULL ) )
390             {
391                 /* FIXME: handle stuff here */
392                 switch( i_key )
393                 {
394                 case 'q':
395                     /* FIXME: need locking ! */
396                     p_main->p_intf->b_die = 1;
397                     break;
398                 }
399             }
400         }
401         /* Mouse click */
402         else if( xevent.type == ButtonPress )
403         {
404             switch( ((XButtonEvent *)&xevent)->button )
405             {
406                 case Button1:
407                     /* in this part we will eventually manage
408                      * clicks for DVD navigation for instance */
409                     break;
410
411                 case Button2:
412                     X11TogglePointer( p_vout );
413                     break;
414
415                 case Button3:
416                     /* FIXME: need locking ! */
417                     p_main->p_intf->b_menu_change = 1;
418                     break;
419             }
420         }
421 #ifdef DEBUG
422         /* Other event */
423         else
424         {
425             intf_DbgMsg( "%p -> unhandled event type %d received",
426                          p_vout, xevent.type );
427         }
428 #endif
429     }
430
431     /* ClientMessage event - only WM_PROTOCOLS with WM_DELETE_WINDOW data
432      * are handled - according to the man pages, the format is always 32
433      * in this case */
434     while( XCheckTypedEvent( p_vout->p_sys->p_display,
435                              ClientMessage, &xevent ) )
436     {
437         if( (xevent.xclient.message_type == p_vout->p_sys->wm_protocols)
438             && (xevent.xclient.data.l[0] == p_vout->p_sys->wm_delete_window ) )
439         {
440             p_main->p_intf->b_die = 1;
441         }
442         else
443         {
444             intf_DbgMsg( "%p -> unhandled ClientMessage received", p_vout );
445         }
446     }
447
448     /*
449      * Handle vout window resizing
450      */
451     if( b_resized )
452     {
453         /* If interface window has been resized, change vout size */
454         intf_DbgMsg( "resizing output window" );
455         p_vout->i_width =  p_vout->p_sys->i_width;
456         p_vout->i_height = p_vout->p_sys->i_height;
457         p_vout->i_changes |= VOUT_SIZE_CHANGE;
458     }
459     else if( (p_vout->i_width  != p_vout->p_sys->i_width) ||
460              (p_vout->i_height != p_vout->p_sys->i_height) )
461     {
462         /* If video output size has changed, change interface window size */
463         intf_DbgMsg( "resizing output window" );
464         p_vout->p_sys->i_width =    p_vout->i_width;
465         p_vout->p_sys->i_height =   p_vout->i_height;
466         XResizeWindow( p_vout->p_sys->p_display, p_vout->p_sys->window,
467                        p_vout->p_sys->i_width, p_vout->p_sys->i_height );
468     }
469     /*
470      * Color/Grayscale or gamma change: in 8bpp, just change the colormap
471      */
472     if( (p_vout->i_changes & VOUT_GRAYSCALE_CHANGE)
473         && (p_vout->i_screen_depth == 8) )
474     {
475         /* FIXME: clear flags ?? */
476     }
477
478     /*
479      * Size change
480      */
481     if( p_vout->i_changes & VOUT_SIZE_CHANGE )
482     {
483         intf_DbgMsg("resizing window");
484         p_vout->i_changes &= ~VOUT_SIZE_CHANGE;
485
486         /* Resize window */
487         XResizeWindow( p_vout->p_sys->p_display, p_vout->p_sys->window,
488                        p_vout->i_width, p_vout->i_height );
489
490         /* Destroy XImages to change their size */
491         vout_End( p_vout );
492
493         /* Recreate XImages. If SysInit failed, the thread can't go on. */
494         if( vout_Init( p_vout ) )
495         {
496             intf_ErrMsg("error: can't resize display");
497             return( 1 );
498        }
499
500         /* Tell the video output thread that it will need to rebuild YUV
501          * tables. This is needed since conversion buffer size may have
502          * changed */
503         p_vout->i_changes |= VOUT_YUV_CHANGE;
504         intf_Msg("vout: video display resized (%dx%d)", p_vout->i_width, p_vout->i_height);
505     }
506
507     return 0;
508 }
509
510 /*****************************************************************************
511  * vout_Display: displays previously rendered output
512  *****************************************************************************
513  * This function send the currently rendered image to X11 server, wait until
514  * it is displayed and switch the two rendering buffer, preparing next frame.
515  *****************************************************************************/
516 static void vout_Display( vout_thread_t *p_vout )
517 {
518     if( p_vout->p_sys->b_shm)                                /* XShm is used */
519     {
520         /* Display rendered image using shared memory extension */
521         XShmPutImage(p_vout->p_sys->p_display, p_vout->p_sys->window, p_vout->p_sys->gc,
522                      p_vout->p_sys->p_ximage[ p_vout->i_buffer_index ],
523                      0, 0, 0, 0,
524                      p_vout->p_sys->p_ximage[ p_vout->i_buffer_index ]->width,
525                      p_vout->p_sys->p_ximage[ p_vout->i_buffer_index ]->height, True);
526
527         /* Send the order to the X server */
528         XSync(p_vout->p_sys->p_display, False);
529     }
530     else                                /* regular X11 capabilities are used */
531     {
532         XPutImage(p_vout->p_sys->p_display, p_vout->p_sys->window, p_vout->p_sys->gc,
533                   p_vout->p_sys->p_ximage[ p_vout->i_buffer_index ],
534                   0, 0, 0, 0,
535                   p_vout->p_sys->p_ximage[ p_vout->i_buffer_index ]->width,
536                   p_vout->p_sys->p_ximage[ p_vout->i_buffer_index ]->height);
537
538         /* Send the order to the X server */
539         XSync(p_vout->p_sys->p_display, False);
540     }
541 }
542
543 /*****************************************************************************
544  * vout_SetPalette: sets an 8 bpp palette
545  *****************************************************************************
546  * This function sets the palette given as an argument. It does not return
547  * anything, but could later send information on which colors it was unable
548  * to set.
549  *****************************************************************************/
550 static void vout_SetPalette( p_vout_thread_t p_vout,
551                              u16 *red, u16 *green, u16 *blue, u16 *transp )
552 {
553     int i, j;
554     XColor p_colors[255];
555
556     intf_DbgMsg( "Palette change called" );
557
558     /* allocate palette */
559     for( i = 0, j = 255; i < 255; i++, j-- )
560     {
561         /* kludge: colors are indexed reversely because color 255 seems
562          * to be reserved for black even if we try to set it to white */
563         p_colors[ i ].pixel = j;
564         p_colors[ i ].pad   = 0;
565         p_colors[ i ].flags = DoRed | DoGreen | DoBlue;
566         p_colors[ i ].red   = red[ j ];
567         p_colors[ i ].blue  = blue[ j ];
568         p_colors[ i ].green = green[ j ];
569     }
570
571     XStoreColors( p_vout->p_sys->p_display,
572                   p_vout->p_sys->colormap, p_colors, 256 );
573 }
574
575 /* following functions are local */
576
577 /*****************************************************************************
578  * X11InitDisplay: open and initialize X11 device
579  *****************************************************************************
580  * Create a window according to video output given size, and set other
581  * properties according to the display properties.
582  *****************************************************************************/
583 static int X11InitDisplay( vout_thread_t *p_vout, char *psz_display )
584 {
585     XPixmapFormatValues *       p_xpixmap_format;          /* pixmap formats */
586     XVisualInfo *               p_xvisual;           /* visuals informations */
587     XVisualInfo                 xvisual_template;         /* visual template */
588     int                         i_count;                       /* array size */
589
590     /* Initialize structure */
591     p_vout->p_sys->i_screen = DefaultScreen( p_vout->p_sys->p_display );
592     p_vout->p_sys->b_shm    = ( XShmQueryExtension( p_vout->p_sys->p_display )
593                                  == True );
594     if( !p_vout->p_sys->b_shm )
595     {
596         intf_Msg("vout: XShm video extension is not available");
597     }
598
599     /* Get screen depth */
600     p_vout->i_screen_depth = XDefaultDepth( p_vout->p_sys->p_display, p_vout->p_sys->i_screen );
601     switch( p_vout->i_screen_depth )
602     {
603     case 8:
604         /*
605          * Screen depth is 8bpp. Use PseudoColor visual with private colormap.
606          */
607         xvisual_template.screen =   p_vout->p_sys->i_screen;
608         xvisual_template.class =    DirectColor;
609         p_xvisual = XGetVisualInfo( p_vout->p_sys->p_display, VisualScreenMask | VisualClassMask,
610                                     &xvisual_template, &i_count );
611         if( p_xvisual == NULL )
612         {
613             intf_ErrMsg("vout error: no PseudoColor visual available");
614             return( 1 );
615         }
616         p_vout->i_bytes_per_pixel = 1;
617         break;
618     case 15:
619     case 16:
620     case 24:
621     default:
622         /*
623          * Screen depth is higher than 8bpp. TrueColor visual is used.
624          */
625         xvisual_template.screen =   p_vout->p_sys->i_screen;
626         xvisual_template.class =    TrueColor;
627         p_xvisual = XGetVisualInfo( p_vout->p_sys->p_display, VisualScreenMask | VisualClassMask,
628                                     &xvisual_template, &i_count );
629         if( p_xvisual == NULL )
630         {
631             intf_ErrMsg("vout error: no TrueColor visual available");
632             return( 1 );
633         }
634         p_vout->i_red_mask =        p_xvisual->red_mask;
635         p_vout->i_green_mask =      p_xvisual->green_mask;
636         p_vout->i_blue_mask =       p_xvisual->blue_mask;
637
638         /* There is no difference yet between 3 and 4 Bpp. The only way to find
639          * the actual number of bytes per pixel is to list supported pixmap
640          * formats. */
641         p_xpixmap_format = XListPixmapFormats( p_vout->p_sys->p_display, &i_count );
642
643         /* Under XFree4.0, the list contains pixmap formats available through 
644          * all video depths ; so we have to check against current depth. */
645         p_vout->i_bytes_per_pixel = 0;
646         for( ; i_count-- ; p_xpixmap_format++ )
647         {
648             if( p_xpixmap_format->depth == p_vout->i_screen_depth )
649             {
650                 if( p_xpixmap_format->bits_per_pixel / 8 > p_vout->i_bytes_per_pixel )
651                 {
652                     p_vout->i_bytes_per_pixel = p_xpixmap_format->bits_per_pixel / 8;
653                 }
654             }
655         }
656         break;
657     }
658     p_vout->p_sys->p_visual = p_xvisual->visual;
659     XFree( p_xvisual );
660
661     return( 0 );
662 }
663
664 /*****************************************************************************
665  * X11CreateImage: create an XImage
666  *****************************************************************************
667  * Create a simple XImage used as a buffer.
668  *****************************************************************************/
669 static int X11CreateImage( vout_thread_t *p_vout, XImage **pp_ximage )
670 {
671     byte_t *    pb_data;                          /* image data storage zone */
672     int         i_quantum;                     /* XImage quantum (see below) */
673
674     /* Allocate memory for image */
675     p_vout->i_bytes_per_line = p_vout->i_width * p_vout->i_bytes_per_pixel;
676     pb_data = (byte_t *) malloc( p_vout->i_bytes_per_line * p_vout->i_height );
677     if( !pb_data )                                                  /* error */
678     {
679         intf_ErrMsg("error: %s", strerror(ENOMEM));
680         return( 1 );
681     }
682
683     /* Optimize the quantum of a scanline regarding its size - the quantum is
684        a diviser of the number of bits between the start of two scanlines. */
685     if( !(( p_vout->i_bytes_per_line ) % 32) )
686     {
687         i_quantum = 32;
688     }
689     else
690     {
691         if( !(( p_vout->i_bytes_per_line ) % 16) )
692         {
693             i_quantum = 16;
694         }
695         else
696         {
697             i_quantum = 8;
698         }
699     }
700
701     /* Create XImage */
702     *pp_ximage = XCreateImage( p_vout->p_sys->p_display, p_vout->p_sys->p_visual,
703                                p_vout->i_screen_depth, ZPixmap, 0, pb_data,
704                                p_vout->i_width, p_vout->i_height, i_quantum, 0);
705     if(! *pp_ximage )                                               /* error */
706     {
707         intf_ErrMsg( "error: XCreateImage() failed" );
708         free( pb_data );
709         return( 1 );
710     }
711
712     return 0;
713 }
714
715 /*****************************************************************************
716  * X11CreateShmImage: create an XImage using shared memory extension
717  *****************************************************************************
718  * Prepare an XImage for DisplayX11ShmImage function.
719  * The order of the operations respects the recommandations of the mit-shm
720  * document by J.Corbet and K.Packard. Most of the parameters were copied from
721  * there.
722  *****************************************************************************/
723 static int X11CreateShmImage( vout_thread_t *p_vout, XImage **pp_ximage,
724                               XShmSegmentInfo *p_shm_info)
725 {
726     /* Create XImage */
727     *pp_ximage =
728         XShmCreateImage( p_vout->p_sys->p_display, p_vout->p_sys->p_visual,
729                          p_vout->i_screen_depth, ZPixmap, 0,
730                          p_shm_info, p_vout->i_width, p_vout->i_height );
731     if(! *pp_ximage )                                               /* error */
732     {
733         intf_ErrMsg("error: XShmCreateImage() failed");
734         return( 1 );
735     }
736
737     /* Allocate shared memory segment - 0777 set the access permission
738      * rights (like umask), they are not yet supported by X servers */
739     p_shm_info->shmid =
740         shmget( IPC_PRIVATE, (*pp_ximage)->bytes_per_line
741                                  * (*pp_ximage)->height, IPC_CREAT | 0777);
742     if( p_shm_info->shmid < 0)                                      /* error */
743     {
744         intf_ErrMsg("error: can't allocate shared image data (%s)",
745                     strerror(errno));
746         XDestroyImage( *pp_ximage );
747         return( 1 );
748     }
749
750     /* Attach shared memory segment to process (read/write) */
751     p_shm_info->shmaddr = (*pp_ximage)->data = shmat(p_shm_info->shmid, 0, 0);
752     if(! p_shm_info->shmaddr )
753     {                                                               /* error */
754         intf_ErrMsg("error: can't attach shared memory (%s)",
755                     strerror(errno));
756         shmctl( p_shm_info->shmid, IPC_RMID, 0 );      /* free shared memory */
757         XDestroyImage( *pp_ximage );
758         return( 1 );
759     }
760
761     /* Mark the shm segment to be removed when there will be no more
762      * attachements, so it is automatic on process exit or after shmdt */
763     shmctl( p_shm_info->shmid, IPC_RMID, 0 );
764
765     /* Attach shared memory segment to X server (read only) */
766     p_shm_info->readOnly = True;
767     if( XShmAttach( p_vout->p_sys->p_display, p_shm_info )
768          == False )                                                 /* error */
769     {
770         intf_ErrMsg("error: can't attach shared memory to X11 server");
771         shmdt( p_shm_info->shmaddr );   /* detach shared memory from process
772                                          * and automatic free */
773         XDestroyImage( *pp_ximage );
774         return( 1 );
775     }
776
777     /* Send image to X server. This instruction is required, since having
778      * built a Shm XImage and not using it causes an error on XCloseDisplay */ 
779     XFlush( p_vout->p_sys->p_display );
780     return( 0 );
781 }
782
783 /*****************************************************************************
784  * X11DestroyImage: destroy an XImage
785  *****************************************************************************
786  * Destroy XImage AND associated data. If pointer is NULL, the image won't be
787  * destroyed (see vout_ManageOutputMethod())
788  *****************************************************************************/
789 static void X11DestroyImage( XImage *p_ximage )
790 {
791     if( p_ximage != NULL )
792     {
793         XDestroyImage( p_ximage );                     /* no free() required */
794     }
795 }
796
797 /*****************************************************************************
798  * X11DestroyShmImage
799  *****************************************************************************
800  * Destroy XImage AND associated data. Detach shared memory segment from
801  * server and process, then free it. If pointer is NULL, the image won't be
802  * destroyed (see vout_ManageOutputMethod())
803  *****************************************************************************/
804 static void X11DestroyShmImage( vout_thread_t *p_vout, XImage *p_ximage,
805                                 XShmSegmentInfo *p_shm_info )
806 {
807     /* If pointer is NULL, do nothing */
808     if( p_ximage == NULL )
809     {
810         return;
811     }
812
813     XShmDetach( p_vout->p_sys->p_display, p_shm_info );/* detach from server */
814     XDestroyImage( p_ximage );
815
816     if( shmdt( p_shm_info->shmaddr ) )  /* detach shared memory from process */
817     {                                   /* also automatic freeing...         */
818         intf_ErrMsg( "error: can't detach shared memory (%s)",
819                      strerror(errno) );
820     }
821 }
822
823
824 /* WAZAAAAAAAAAAA */
825
826 /*****************************************************************************
827  * X11CreateWindow: open and set-up X11 main window
828  *****************************************************************************/
829 static int X11CreateWindow( vout_thread_t *p_vout )
830 {
831     XSizeHints              xsize_hints;
832     XSetWindowAttributes    xwindow_attributes;
833     XGCValues               xgcvalues;
834     XEvent                  xevent;
835     boolean_t               b_expose;
836     boolean_t               b_configure_notify;
837     boolean_t               b_map_notify;
838
839     /* Set main window's size */
840     p_vout->p_sys->i_width =  main_GetIntVariable( VOUT_WIDTH_VAR,
841                                                    VOUT_WIDTH_DEFAULT );
842     p_vout->p_sys->i_height = main_GetIntVariable( VOUT_HEIGHT_VAR,
843                                                    VOUT_HEIGHT_DEFAULT );
844
845     /* Prepare window manager hints and properties */
846     xsize_hints.base_width          = p_vout->p_sys->i_width;
847     xsize_hints.base_height         = p_vout->p_sys->i_height;
848     xsize_hints.flags               = PSize;
849     p_vout->p_sys->wm_protocols     = XInternAtom( p_vout->p_sys->p_display,
850                                                    "WM_PROTOCOLS", True );
851     p_vout->p_sys->wm_delete_window = XInternAtom( p_vout->p_sys->p_display,
852                                                    "WM_DELETE_WINDOW", True );
853
854     /* Prepare window attributes */
855     xwindow_attributes.backing_store = Always;       /* save the hidden part */
856     xwindow_attributes.background_pixel = WhitePixel( p_vout->p_sys->p_display,
857                                                       p_vout->p_sys->i_screen );
858
859     xwindow_attributes.event_mask = ExposureMask | StructureNotifyMask;
860
861     /* Create the window and set hints - the window must receive ConfigureNotify
862      * events, and, until it is displayed, Expose and MapNotify events. */
863     p_vout->p_sys->window =
864             XCreateWindow( p_vout->p_sys->p_display,
865                            DefaultRootWindow( p_vout->p_sys->p_display ),
866                            0, 0,
867                            p_vout->p_sys->i_width, p_vout->p_sys->i_height, 1,
868                            0, InputOutput, 0,
869                            CWBackingStore | CWBackPixel | CWEventMask,
870                            &xwindow_attributes );
871
872     /* Set window manager hints and properties: size hints, command,
873      * window's name, and accepted protocols */
874     XSetWMNormalHints( p_vout->p_sys->p_display, p_vout->p_sys->window,
875                        &xsize_hints );
876     XSetCommand( p_vout->p_sys->p_display, p_vout->p_sys->window,
877                  p_main->ppsz_argv, p_main->i_argc );
878     XStoreName( p_vout->p_sys->p_display, p_vout->p_sys->window, VOUT_TITLE );
879
880     if( (p_vout->p_sys->wm_protocols == None)        /* use WM_DELETE_WINDOW */
881         || (p_vout->p_sys->wm_delete_window == None)
882         || !XSetWMProtocols( p_vout->p_sys->p_display, p_vout->p_sys->window,
883                              &p_vout->p_sys->wm_delete_window, 1 ) )
884     {
885         /* WM_DELETE_WINDOW is not supported by window manager */
886         intf_Msg("intf error: missing or bad window manager - please exit program kindly.");
887     }
888
889     /* Creation of a graphic context that doesn't generate a GraphicsExpose
890      * event when using functions like XCopyArea */
891     xgcvalues.graphics_exposures = False;
892     p_vout->p_sys->gc =  XCreateGC( p_vout->p_sys->p_display, p_vout->p_sys->window,
893                                     GCGraphicsExposures, &xgcvalues);
894
895     /* Send orders to server, and wait until window is displayed - three
896      * events must be received: a MapNotify event, an Expose event allowing
897      * drawing in the window, and a ConfigureNotify to get the window
898      * dimensions. Once those events have been received, only ConfigureNotify
899      * events need to be received. */
900     b_expose = 0;
901     b_configure_notify = 0;
902     b_map_notify = 0;
903     XMapWindow( p_vout->p_sys->p_display, p_vout->p_sys->window);
904     do
905     {
906         XNextEvent( p_vout->p_sys->p_display, &xevent);
907         if( (xevent.type == Expose)
908             && (xevent.xexpose.window == p_vout->p_sys->window) )
909         {
910             b_expose = 1;
911         }
912         else if( (xevent.type == MapNotify)
913                  && (xevent.xmap.window == p_vout->p_sys->window) )
914         {
915             b_map_notify = 1;
916         }
917         else if( (xevent.type == ConfigureNotify)
918                  && (xevent.xconfigure.window == p_vout->p_sys->window) )
919         {
920             b_configure_notify = 1;
921             p_vout->p_sys->i_width = xevent.xconfigure.width;
922             p_vout->p_sys->i_height = xevent.xconfigure.height;
923         }
924     } while( !( b_expose && b_configure_notify && b_map_notify ) );
925
926     XSelectInput( p_vout->p_sys->p_display, p_vout->p_sys->window,
927                   StructureNotifyMask | KeyPressMask | ButtonPressMask );
928
929     if( XDefaultDepth(p_vout->p_sys->p_display, p_vout->p_sys->i_screen) == 8 )
930     {
931         /* Allocate a new palette */
932         p_vout->p_sys->colormap = XCreateColormap( p_vout->p_sys->p_display,
933                               DefaultRootWindow( p_vout->p_sys->p_display ),
934                               DefaultVisual( p_vout->p_sys->p_display,
935                                              p_vout->p_sys->i_screen ),
936                               AllocAll );
937
938         xwindow_attributes.colormap = p_vout->p_sys->colormap;
939         XChangeWindowAttributes( p_vout->p_sys->p_display,
940                                  p_vout->p_sys->window,
941                                  CWColormap, &xwindow_attributes );
942     }
943
944     /* At this stage, the window is open, displayed, and ready to
945      * receive data */
946     return( 0 );
947 }
948
949 /*****************************************************************************
950  * X11EnableScreenSaver: enable screen saver
951  *****************************************************************************
952  * This function enable the screen saver on a display after it had been
953  * disabled by XDisableScreenSaver. Both functions use a counter mechanism to
954  * know wether the screen saver can be activated or not: if n successive calls
955  * are made to XDisableScreenSaver, n successive calls to XEnableScreenSaver
956  * will be required before the screen saver could effectively be activated.
957  *****************************************************************************/
958 void X11EnableScreenSaver( vout_thread_t *p_vout )
959 {
960     intf_DbgMsg( "intf: enabling screen saver" );
961     XSetScreenSaver( p_vout->p_sys->p_display, p_vout->p_sys->i_ss_timeout,
962                      p_vout->p_sys->i_ss_interval,
963                      p_vout->p_sys->i_ss_blanking,
964                      p_vout->p_sys->i_ss_exposure );
965 }
966
967 /*****************************************************************************
968  * X11DisableScreenSaver: disable screen saver
969  *****************************************************************************
970  * See XEnableScreenSaver
971  *****************************************************************************/
972 void X11DisableScreenSaver( vout_thread_t *p_vout )
973 {
974     /* Save screen saver informations */
975     XGetScreenSaver( p_vout->p_sys->p_display, &p_vout->p_sys->i_ss_timeout,
976                      &p_vout->p_sys->i_ss_interval,
977                      &p_vout->p_sys->i_ss_blanking,
978                      &p_vout->p_sys->i_ss_exposure );
979
980     /* Disable screen saver */
981     intf_DbgMsg("intf: disabling screen saver");
982     XSetScreenSaver( p_vout->p_sys->p_display, 0,
983                      p_vout->p_sys->i_ss_interval, p_vout->p_sys->i_ss_blanking,
984                      p_vout->p_sys->i_ss_exposure );
985 }
986
987 /*****************************************************************************
988  * X11TogglePointer: hide or show the mouse pointer
989  *****************************************************************************
990  * This function hides the X pointer if it is visible by putting it at
991  * coordinates (32,32) and setting the pointer sprite to a blank one. To
992  * show it again, we disable the sprite and restore the original coordinates.
993  *****************************************************************************/
994 void X11TogglePointer( vout_thread_t *p_vout )
995 {
996     static Cursor cursor;
997     static boolean_t b_cursor = 0;
998
999     if( p_vout->p_sys->b_mouse )
1000     {
1001         p_vout->p_sys->b_mouse = 0;
1002
1003         if( !b_cursor )
1004         {
1005             XColor color;
1006             Pixmap blank = XCreatePixmap( p_vout->p_sys->p_display,
1007                                DefaultRootWindow(p_vout->p_sys->p_display),
1008                                1, 1, 1 );
1009
1010             XParseColor( p_vout->p_sys->p_display,
1011                          XCreateColormap( p_vout->p_sys->p_display,
1012                                           DefaultRootWindow(
1013                                                   p_vout->p_sys->p_display ),
1014                                           DefaultVisual(
1015                                                   p_vout->p_sys->p_display,
1016                                                   p_vout->p_sys->i_screen ),
1017                                           AllocNone ),
1018                          "black", &color );
1019
1020             cursor = XCreatePixmapCursor( p_vout->p_sys->p_display,
1021                            blank, blank, &color, &color, 1, 1 );
1022
1023             b_cursor = 1;
1024         }
1025         XDefineCursor( p_vout->p_sys->p_display,
1026                        p_vout->p_sys->window, cursor );
1027     }
1028     else
1029     {
1030         p_vout->p_sys->b_mouse = 1;
1031
1032         XUndefineCursor( p_vout->p_sys->p_display, p_vout->p_sys->window );
1033     }
1034 }