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