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