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