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