]> git.sesse.net Git - vlc/blob - modules/video_output/mga/xmga.c
* now 0.6.0-cvs
[vlc] / modules / video_output / mga / xmga.c
1 /*****************************************************************************
2  * xmga.c : X11 MGA plugin for vlc
3  *****************************************************************************
4  * Copyright (C) 1998-2001 VideoLAN
5  * $Id: xmga.c,v 1.6 2003/05/15 22:27:38 massiot Exp $
6  *
7  * Authors: Vincent Seguin <seguin@via.ecp.fr>
8  *          Samuel Hocevar <sam@zoy.org>
9  *          Gildas Bazin <gbazin@netcourrier.com>
10  *      
11  * This program is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation; either version 2 of the License, or
14  * (at your option) any later version.
15  * 
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
24  *****************************************************************************/
25
26 /*****************************************************************************
27  * Preamble
28  *****************************************************************************/
29 #include <errno.h>                                                 /* ENOMEM */
30 #include <stdlib.h>                                                /* free() */
31 #include <string.h>                                            /* strerror() */
32
33 #include <vlc/vlc.h>
34 #include <vlc/intf.h>
35 #include <vlc/vout.h>
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 #ifndef WIN32
45 #include <netinet/in.h>                               /* BSD: struct in_addr */
46 #endif
47
48 #include <sys/shm.h>                                   /* shmget(), shmctl() */
49 #include <X11/Xlib.h>
50 #include <X11/Xutil.h>
51 #include <X11/keysym.h>
52 #include <X11/extensions/XShm.h>
53 #include <X11/extensions/dpms.h>
54
55 //#include "mga.h"
56
57 /*****************************************************************************
58  * Local prototypes
59  *****************************************************************************/
60 static int  Create    ( vlc_object_t * );
61 static void Destroy   ( vlc_object_t * );
62
63 static int  Init      ( vout_thread_t * );
64 static void End       ( vout_thread_t * );                         
65 static int  Manage    ( vout_thread_t * );               
66 static void MGADisplay( vout_thread_t *, picture_t * );            
67
68 static int  CreateWindow   ( vout_thread_t * );
69 static void DestroyWindow  ( vout_thread_t * );
70
71 static int  NewPicture     ( vout_thread_t *, picture_t * );
72 static void FreePicture    ( vout_thread_t *, picture_t * );
73
74 static void ToggleFullScreen      ( vout_thread_t * );
75
76 static void EnableXScreenSaver    ( vout_thread_t * );
77 static void DisableXScreenSaver   ( vout_thread_t * );
78
79 static void CreateCursor   ( vout_thread_t * );
80 static void DestroyCursor  ( vout_thread_t * );
81 static void ToggleCursor   ( vout_thread_t * );
82
83 /*****************************************************************************
84  * Module descriptor
85  *****************************************************************************/
86
87 #define ALT_FS_TEXT N_("Alternate fullscreen method")
88 #define ALT_FS_LONGTEXT N_( \
89     "There are two ways to make a fullscreen window, unfortunately each one " \
90     "has its drawbacks.\n" \
91     "1) Let the window manager handle your fullscreen window (default). But " \
92     "things like taskbars will likely show on top of the video.\n" \
93     "2) Completly bypass the window manager, but then nothing will be able " \
94     "to show on top of the video.")
95
96 #define DISPLAY_TEXT N_("X11 display name")
97 #define DISPLAY_LONGTEXT N_( \
98     "Specify the X11 hardware display you want to use. By default VLC will " \
99     "use the value of the DISPLAY environment variable.")
100
101 vlc_module_begin();  
102     add_category_hint( N_("Miscellaneous"), NULL, VLC_TRUE );
103     add_string( "xmga-display", NULL, NULL, DISPLAY_TEXT, DISPLAY_LONGTEXT, VLC_TRUE );
104     add_bool( "xmga-altfullscreen", 0, NULL, ALT_FS_TEXT, ALT_FS_LONGTEXT, VLC_TRUE );
105     set_description( _("X11 MGA video output") );
106     set_capability( "video output", 60 );
107     set_callbacks( Create, Destroy );
108 vlc_module_end();
109
110 /*****************************************************************************
111  * vout_sys_t: video output method descriptor
112  *****************************************************************************
113  * This structure is part of the video output thread descriptor.
114  * It describes the X11 and XVideo specific properties of an output thread.
115  *****************************************************************************/
116 struct vout_sys_t
117 {
118     /* Internal settings and properties */
119     Display *           p_display;                        /* display pointer */
120
121     Visual *            p_visual;                          /* visual pointer */
122     int                 i_screen;                           /* screen number */
123     Window              window;                               /* root window */
124     GC                  gc;              /* graphic context instance handler */
125
126     vlc_bool_t          b_shm;               /* shared memory extension flag */
127
128 #ifdef MODULE_NAME_IS_xvideo
129     Window              yuv_window;   /* sub-window for displaying yuv video
130                                                                         data */
131     int                 i_xvport;
132 #else
133     Colormap            colormap;               /* colormap used (8bpp only) */
134
135     int                 i_screen_depth;
136     int                 i_bytes_per_pixel;
137     int                 i_bytes_per_line;
138     int                 i_red_mask;
139     int                 i_green_mask;
140     int                 i_blue_mask;
141 #endif
142
143     /* X11 generic properties */
144     Atom                wm_protocols;
145     Atom                wm_delete_window;
146
147     int                 i_width;                     /* width of main window */
148     int                 i_height;                   /* height of main window */
149     vlc_bool_t          b_altfullscreen;          /* which fullscreen method */
150
151     /* Backup of window position and size before fullscreen switch */
152     int                 i_width_backup;
153     int                 i_height_backup;
154     int                 i_xpos_backup;
155     int                 i_ypos_backup;
156     int                 i_width_backup_2;
157     int                 i_height_backup_2;
158     int                 i_xpos_backup_2;
159     int                 i_ypos_backup_2;
160
161     /* Screen saver properties */
162     int                 i_ss_timeout;                             /* timeout */
163     int                 i_ss_interval;           /* interval between changes */
164     int                 i_ss_blanking;                      /* blanking mode */
165     int                 i_ss_exposure;                      /* exposure mode */
166     BOOL                b_ss_dpms;                              /* DPMS mode */
167
168     /* Mouse pointer properties */
169     vlc_bool_t          b_mouse_pointer_visible;
170     mtime_t             i_time_mouse_last_moved; /* used to auto-hide pointer*/
171     Cursor              blank_cursor;                   /* the hidden cursor */
172     mtime_t             i_time_button_last_pressed;   /* to track dbl-clicks */
173     Pixmap              cursor_pixmap;
174 };
175
176 /*****************************************************************************
177  * mwmhints_t: window manager hints
178  *****************************************************************************
179  * Fullscreen needs to be able to hide the wm decorations so we provide
180  * this structure to make it easier.
181  *****************************************************************************/
182 #define MWM_HINTS_DECORATIONS   (1L << 1)
183 #define PROP_MWM_HINTS_ELEMENTS 5
184 typedef struct mwmhints_t
185 {
186     u32 flags;
187     u32 functions;
188     u32 decorations;
189     s32 input_mode;
190     u32 status;
191 } mwmhints_t;
192
193 /*****************************************************************************
194  * Chroma defines
195  *****************************************************************************/
196 #ifdef MODULE_NAME_IS_xvideo
197 #   define MAX_DIRECTBUFFERS 5
198 #else
199 #   define MAX_DIRECTBUFFERS 2
200 #endif
201
202 /*****************************************************************************
203  * Create: allocate X11 video thread output method
204  *****************************************************************************
205  * This function allocate and initialize a X11 vout method. It uses some of the
206  * vout properties to choose the window size, and change them according to the
207  * actual properties of the display.
208  *****************************************************************************/
209 static int Create( vlc_object_t *p_this )
210 {   
211     vout_thread_t *p_vout = (vout_thread_t *)p_this; 
212     char *psz_display;
213
214     /* Allocate structure */
215     p_vout->p_sys = malloc( sizeof( vout_sys_t ) );
216     if( p_vout->p_sys == NULL )
217     {
218         msg_Err( p_vout, "out of memory" );
219         return( 1 );
220     }
221
222     /* Open display, unsing the "display" config variable or the DISPLAY
223      * environment variable */
224     psz_display = config_GetPsz( p_vout, "xmga-display" );
225     p_vout->p_sys->p_display = XOpenDisplay( psz_display );
226
227     if( p_vout->p_sys->p_display == NULL )                          /* error */
228     {
229         msg_Err( p_vout, "cannot open display %s",
230                          XDisplayName( psz_display ) );
231         free( p_vout->p_sys );
232         if( psz_display ) free( psz_display );
233         return( 1 );
234     }
235     if( psz_display ) free( psz_display );
236
237     p_vout->p_sys->i_screen = DefaultScreen( p_vout->p_sys->p_display );
238
239     /* Create blank cursor (for mouse cursor autohiding) */
240     p_vout->p_sys->b_mouse_pointer_visible = 1;
241     CreateCursor( p_vout );
242
243     /* Spawn base window - this window will include the video output window,
244      * but also command buttons, subtitles and other indicators */
245     if( CreateWindow( p_vout ) )
246     {
247         msg_Err( p_vout, "cannot create X11 window" );
248         DestroyCursor( p_vout );
249         XCloseDisplay( p_vout->p_sys->p_display );
250         free( p_vout->p_sys );
251         return( 1 );
252     }
253
254     /* Disable screen saver */
255     DisableXScreenSaver( p_vout );
256
257     /* Misc init */
258     p_vout->p_sys->b_altfullscreen = 0;
259
260     p_vout->pf_init = Init;
261     p_vout->pf_end = End;
262     p_vout->pf_manage = Manage;
263     p_vout->pf_render = NULL;
264     p_vout->pf_display = MGADisplay;
265
266     return( 0 );
267 }
268
269 /*****************************************************************************
270  * Destroy: destroy X11 video thread output method
271  *****************************************************************************
272  * Terminate an output method created by Create
273  *****************************************************************************/
274 static void Destroy( vlc_object_t *p_this )
275 {   
276     vout_thread_t *p_vout = (vout_thread_t *)p_this;
277
278     /* Restore cursor if it was blanked */
279     if( !p_vout->p_sys->b_mouse_pointer_visible )
280     {
281         ToggleCursor( p_vout );
282     }
283
284     DestroyCursor( p_vout );
285     EnableXScreenSaver( p_vout );
286     DestroyWindow( p_vout );
287
288     XCloseDisplay( p_vout->p_sys->p_display );
289
290     /* Destroy structure */
291     free( p_vout->p_sys );
292 }
293
294 /*****************************************************************************
295  * Init: initialize X11 video thread output method
296  *****************************************************************************
297  * This function create the XImages needed by the output thread. It is called
298  * at the beginning of the thread, but also each time the window is resized.
299  *****************************************************************************/
300 static int Init( vout_thread_t *p_vout )
301 {
302     int i_index;
303     picture_t *p_pic;
304
305     I_OUTPUTPICTURES = 0;
306
307 #ifdef MODULE_NAME_IS_xvideo
308     /* Initialize the output structure; we already found an XVideo port,
309      * and the corresponding chroma we will be using. Since we can
310      * arbitrary scale, stick to the coordinates and aspect. */
311     p_vout->output.i_width  = p_vout->render.i_width;
312     p_vout->output.i_height = p_vout->render.i_height;
313     p_vout->output.i_aspect = p_vout->render.i_aspect;
314
315 #else
316     /* Initialize the output structure: RGB with square pixels, whatever
317      * the input format is, since it's the only format we know */
318     switch( p_vout->p_sys->i_screen_depth )
319     {
320         case 8: /* FIXME: set the palette */
321             p_vout->output.i_chroma = VLC_FOURCC('R','G','B','2'); break;
322         case 15:
323             p_vout->output.i_chroma = VLC_FOURCC('R','V','1','5'); break;
324         case 16:
325             p_vout->output.i_chroma = VLC_FOURCC('R','V','1','6'); break;
326         case 24:
327             p_vout->output.i_chroma = VLC_FOURCC('R','V','2','4'); break;
328         case 32:
329             p_vout->output.i_chroma = VLC_FOURCC('R','V','3','2'); break;
330         default:
331             msg_Err( p_vout, "unknown screen depth %i",
332                              p_vout->p_sys->i_screen_depth );
333             return( 0 );
334     }
335
336     p_vout->output.i_width = p_vout->p_sys->i_width;
337     p_vout->output.i_height = p_vout->p_sys->i_height;
338
339     /* Assume we have square pixels */
340     p_vout->output.i_aspect = p_vout->p_sys->i_width
341                                * VOUT_ASPECT_FACTOR / p_vout->p_sys->i_height;
342 #endif
343
344     /* Try to initialize up to MAX_DIRECTBUFFERS direct buffers */
345     while( I_OUTPUTPICTURES < MAX_DIRECTBUFFERS )
346     {
347         p_pic = NULL;
348
349         /* Find an empty picture slot */
350         for( i_index = 0 ; i_index < VOUT_MAX_PICTURES ; i_index++ )
351         {
352             if( p_vout->p_picture[ i_index ].i_status == FREE_PICTURE )
353             {
354                 p_pic = p_vout->p_picture + i_index;
355                 break;
356             }
357         }
358
359         /* Allocate the picture */
360         if( p_pic == NULL || NewPicture( p_vout, p_pic ) )
361         {
362             break;
363         }
364
365         p_pic->i_status = DESTROYED_PICTURE;
366         p_pic->i_type   = DIRECT_PICTURE;
367
368         PP_OUTPUTPICTURE[ I_OUTPUTPICTURES ] = p_pic;
369
370         I_OUTPUTPICTURES++;
371     }
372
373     return( 0 );
374 }
375
376 /*****************************************************************************
377  * MGADisplay: displays previously rendered output
378  *****************************************************************************
379  * This function sends the currently rendered image to X11 server.
380  * (The Xv extension takes care of "double-buffering".)
381  *****************************************************************************/
382 static void MGADisplay( vout_thread_t *p_vout, picture_t *p_pic )
383 {
384     int i_width, i_height, i_x, i_y;
385
386     vout_PlacePicture( p_vout, p_vout->p_sys->i_width, p_vout->p_sys->i_height,
387                        &i_x, &i_y, &i_width, &i_height );
388 }
389
390 /*****************************************************************************
391  * Manage: handle X11 events
392  *****************************************************************************
393  * This function should be called regularly by video output thread. It manages
394  * X11 events and allows window resizing. It returns a non null value on
395  * error.
396  *****************************************************************************/
397 static int Manage( vout_thread_t *p_vout )
398 {
399     XEvent      xevent;                                         /* X11 event */
400     vlc_bool_t  b_resized;                        /* window has been resized */
401     char        i_key;                                    /* ISO Latin-1 key */
402     KeySym      x_key_symbol;
403
404     /* Handle X11 events: ConfigureNotify events are parsed to know if the
405      * output window's size changed, MapNotify and UnmapNotify to know if the
406      * window is mapped (and if the display is useful), and ClientMessages
407      * to intercept window destruction requests */
408
409     b_resized = 0;
410     while( XCheckWindowEvent( p_vout->p_sys->p_display, p_vout->p_sys->window,
411                               StructureNotifyMask | KeyPressMask |
412                               ButtonPressMask | ButtonReleaseMask | 
413                               PointerMotionMask | Button1MotionMask , &xevent )
414            == True )
415     {
416         /* ConfigureNotify event: prepare  */
417         if( (xevent.type == ConfigureNotify)
418           && ((xevent.xconfigure.width != p_vout->p_sys->i_width)
419              || (xevent.xconfigure.height != p_vout->p_sys->i_height)) )
420         {
421             /* Update dimensions */
422             b_resized = 1;
423             p_vout->i_changes |= VOUT_SIZE_CHANGE;
424             p_vout->p_sys->i_width = xevent.xconfigure.width;
425             p_vout->p_sys->i_height = xevent.xconfigure.height;
426         }
427         /* Keyboard event */
428         else if( xevent.type == KeyPress )
429         {
430             /* We may have keys like F1 trough F12, ESC ... */
431             x_key_symbol = XKeycodeToKeysym( p_vout->p_sys->p_display,
432                                              xevent.xkey.keycode, 0 );
433             switch( x_key_symbol )
434             {
435             case XK_Escape:
436                 p_vout->p_vlc->b_die = 1;
437                 break;
438             case XK_Menu:
439                 p_vout->p_vlc->p_intf->b_menu_change = 1;
440                 break;
441             case XK_Left:
442                 input_Seek( p_vout, -5, INPUT_SEEK_SECONDS | INPUT_SEEK_CUR );
443                 break;
444             case XK_Right:
445                 input_Seek( p_vout, 5, INPUT_SEEK_SECONDS | INPUT_SEEK_CUR );
446                 break;
447             case XK_Up:
448                 input_Seek( p_vout, 60, INPUT_SEEK_SECONDS | INPUT_SEEK_CUR );
449                 break;
450             case XK_Down:
451                 input_Seek( p_vout, -60, INPUT_SEEK_SECONDS | INPUT_SEEK_CUR );
452                 break;
453             case XK_Home:
454                 input_Seek( p_vout, 0, INPUT_SEEK_BYTES | INPUT_SEEK_SET );
455                 break;
456             case XK_End:
457                 input_Seek( p_vout, 0, INPUT_SEEK_BYTES | INPUT_SEEK_END );
458                 break;
459             case XK_Page_Up:
460                 input_Seek( p_vout, 900, INPUT_SEEK_SECONDS | INPUT_SEEK_CUR );
461                 break;
462             case XK_Page_Down:
463                 input_Seek( p_vout, -900, INPUT_SEEK_SECONDS | INPUT_SEEK_CUR );
464                 break;
465             case XK_space:
466                 input_SetStatus( p_input_bank->pp_input[0],
467                                  INPUT_STATUS_PAUSE );
468                 break;
469
470             default:
471                 /* "Normal Keys"
472                  * The reason why I use this instead of XK_0 is that 
473                  * with XLookupString, we don't have to care about
474                  * keymaps. */
475
476                 if( XLookupString( &xevent.xkey, &i_key, 1, NULL, NULL ) )
477                 {
478                 /* FIXME: handle stuff here */
479                     switch( i_key )
480                     {
481                     case 'q':
482                     case 'Q':
483                         p_vout->p_vlc->b_die = 1;
484                         break;
485                     case 'f':
486                     case 'F':
487                         p_vout->i_changes |= VOUT_FULLSCREEN_CHANGE;
488                         break;
489
490                     default:
491                         break;
492                     }
493                 }
494                 break;
495             }
496         }
497         /* Mouse click */
498         else if( xevent.type == ButtonPress )
499         {
500             switch( ((XButtonEvent *)&xevent)->button )
501             {
502                 case Button1:
503                     /* In this part we will eventually manage
504                      * clicks for DVD navigation for instance. */
505
506                     /* detect double-clicks */
507                     if( ( ((XButtonEvent *)&xevent)->time -
508                           p_vout->p_sys->i_time_button_last_pressed ) < 300 )
509                     {
510                       p_vout->i_changes |= VOUT_FULLSCREEN_CHANGE;
511                     }
512
513                     p_vout->p_sys->i_time_button_last_pressed =
514                         ((XButtonEvent *)&xevent)->time;
515                     break;
516
517                 case Button4:
518                     input_Seek( p_vout, 15, INPUT_SEEK_SECONDS | INPUT_SEEK_CUR );
519                     break;
520
521                 case Button5:
522                     input_Seek( p_vout, -15, INPUT_SEEK_SECONDS | INPUT_SEEK_CUR );
523                     break;
524             }
525         }
526         /* Mouse release */
527         else if( xevent.type == ButtonRelease )
528         {
529             switch( ((XButtonEvent *)&xevent)->button )
530             {
531                 case Button3:
532                     /* FIXME: need locking ! */
533                     p_vout->p_vlc->p_intf->b_menu_change = 1;
534                     break;
535             }
536         }
537         /* Mouse move */
538         else if( xevent.type == MotionNotify )
539         {
540             p_vout->p_sys->i_time_mouse_last_moved = mdate();
541             if( ! p_vout->p_sys->b_mouse_pointer_visible )
542             {
543                 ToggleCursor( p_vout ); 
544             }
545         }
546         /* Other event */
547         else
548         {
549             msg_Warn( p_vout, "unhandled event %d received", xevent.type );
550         }
551     }
552
553     /* ClientMessage event - only WM_PROTOCOLS with WM_DELETE_WINDOW data
554      * are handled - according to the man pages, the format is always 32
555      * in this case */
556     while( XCheckTypedEvent( p_vout->p_sys->p_display,
557                              ClientMessage, &xevent ) )
558     {
559         if( (xevent.xclient.message_type == p_vout->p_sys->wm_protocols)
560             && (xevent.xclient.data.l[0] == p_vout->p_sys->wm_delete_window ) )
561         {
562             p_vout->p_vlc->b_die = 1;
563         }
564     }
565
566     /*
567      * Fullscreen Change
568      */
569     if ( p_vout->i_changes & VOUT_FULLSCREEN_CHANGE )
570     {
571         ToggleFullScreen( p_vout );
572         p_vout->i_changes &= ~VOUT_FULLSCREEN_CHANGE;
573
574     }
575
576     /*
577      * Size change
578      */
579     if( p_vout->i_changes & VOUT_SIZE_CHANGE )
580     {
581         int i_width, i_height, i_x, i_y;
582
583         p_vout->i_changes &= ~VOUT_SIZE_CHANGE;
584
585         msg_Dbg( p_vout, "video display resized (%dx%d)",
586                  p_vout->p_sys->i_width, p_vout->p_sys->i_height );
587  
588         vout_PlacePicture( p_vout, p_vout->p_sys->i_width,
589                            p_vout->p_sys->i_height,
590                            &i_x, &i_y, &i_width, &i_height );
591     }
592
593     /* Autohide Cursour */
594     if( mdate() - p_vout->p_sys->i_time_mouse_last_moved > 2000000 )
595     {
596         /* Hide the mouse automatically */
597         if( p_vout->p_sys->b_mouse_pointer_visible )
598         {
599             ToggleCursor( p_vout ); 
600         }
601     }
602
603     return 0;
604 }
605
606 /*****************************************************************************
607  * End: terminate X11 video thread output method
608  *****************************************************************************
609  * Destroy the X11 XImages created by Init. It is called at the end of
610  * the thread, but also each time the window is resized.
611  *****************************************************************************/
612 static void End( vout_thread_t *p_vout )
613 {
614     int i_index;
615
616     /* Free the direct buffers we allocated */
617     for( i_index = I_OUTPUTPICTURES ; i_index ; )
618     {
619         i_index--;
620         FreePicture( p_vout, PP_OUTPUTPICTURE[ i_index ] );
621     }
622 }
623
624 /* following functions are local */
625
626 /*****************************************************************************
627  * CreateWindow: open and set-up X11 main window
628  *****************************************************************************/
629 static int CreateWindow( vout_thread_t *p_vout )
630 {
631     XSizeHints              xsize_hints;
632     XSetWindowAttributes    xwindow_attributes;
633     XGCValues               xgcvalues;
634     XEvent                  xevent;
635
636     vlc_bool_t              b_expose;
637     vlc_bool_t              b_configure_notify;
638     vlc_bool_t              b_map_notify;
639
640     /* Set main window's size */
641     p_vout->p_sys->i_width = p_vout->i_window_width;
642     p_vout->p_sys->i_height = p_vout->i_window_height;
643
644     /* Prepare window manager hints and properties */
645     xsize_hints.base_width          = p_vout->p_sys->i_width;
646     xsize_hints.base_height         = p_vout->p_sys->i_height;
647     xsize_hints.flags               = PSize;
648     p_vout->p_sys->wm_protocols     = XInternAtom( p_vout->p_sys->p_display,
649                                                    "WM_PROTOCOLS", True );
650     p_vout->p_sys->wm_delete_window = XInternAtom( p_vout->p_sys->p_display,
651                                                    "WM_DELETE_WINDOW", True );
652
653     /* Prepare window attributes */
654     xwindow_attributes.backing_store = Always;       /* save the hidden part */
655     xwindow_attributes.background_pixel = BlackPixel(p_vout->p_sys->p_display,
656                                                      p_vout->p_sys->i_screen);
657     xwindow_attributes.event_mask = ExposureMask | StructureNotifyMask;
658     
659
660     /* Create the window and set hints - the window must receive
661      * ConfigureNotify events, and until it is displayed, Expose and
662      * MapNotify events. */
663
664     p_vout->p_sys->window =
665         XCreateWindow( p_vout->p_sys->p_display,
666                        DefaultRootWindow( p_vout->p_sys->p_display ),
667                        0, 0,
668                        p_vout->p_sys->i_width,
669                        p_vout->p_sys->i_height,
670                        0,
671                        0, InputOutput, 0,
672                        CWBackingStore | CWBackPixel | CWEventMask,
673                        &xwindow_attributes );
674
675     /* Set window manager hints and properties: size hints, command,
676      * window's name, and accepted protocols */
677     XSetWMNormalHints( p_vout->p_sys->p_display, p_vout->p_sys->window,
678                        &xsize_hints );
679     XSetCommand( p_vout->p_sys->p_display, p_vout->p_sys->window,
680                  p_vout->p_vlc->ppsz_argv, p_vout->p_vlc->i_argc );
681     XStoreName( p_vout->p_sys->p_display, p_vout->p_sys->window,
682                 VOUT_TITLE " (XMGA output)"
683               );
684
685     if( (p_vout->p_sys->wm_protocols == None)        /* use WM_DELETE_WINDOW */
686         || (p_vout->p_sys->wm_delete_window == None)
687         || !XSetWMProtocols( p_vout->p_sys->p_display, p_vout->p_sys->window,
688                              &p_vout->p_sys->wm_delete_window, 1 ) )
689     {
690         /* WM_DELETE_WINDOW is not supported by window manager */
691         msg_Err( p_vout, "missing or bad window manager" );
692     } 
693
694     /* Creation of a graphic context that doesn't generate a GraphicsExpose
695      * event when using functions like XCopyArea */
696     xgcvalues.graphics_exposures = False;
697     p_vout->p_sys->gc = XCreateGC( p_vout->p_sys->p_display,
698                                    p_vout->p_sys->window,
699                                    GCGraphicsExposures, &xgcvalues);
700
701     /* Send orders to server, and wait until window is displayed - three
702      * events must be received: a MapNotify event, an Expose event allowing
703      * drawing in the window, and a ConfigureNotify to get the window
704      * dimensions. Once those events have been received, only ConfigureNotify
705      * events need to be received. */
706     b_expose = 0;
707     b_configure_notify = 0;
708     b_map_notify = 0;
709     XMapWindow( p_vout->p_sys->p_display, p_vout->p_sys->window);
710     do
711     {
712         XNextEvent( p_vout->p_sys->p_display, &xevent);
713         if( (xevent.type == Expose)
714             && (xevent.xexpose.window == p_vout->p_sys->window) )
715         {
716             b_expose = 1;
717         }
718         else if( (xevent.type == MapNotify)
719                  && (xevent.xmap.window == p_vout->p_sys->window) )
720         {
721             b_map_notify = 1;
722         }
723         else if( (xevent.type == ConfigureNotify)
724                  && (xevent.xconfigure.window == p_vout->p_sys->window) )
725         {
726             b_configure_notify = 1;
727             p_vout->p_sys->i_width = xevent.xconfigure.width;
728             p_vout->p_sys->i_height = xevent.xconfigure.height;
729         }
730     } while( !( b_expose && b_configure_notify && b_map_notify ) );
731
732     XSelectInput( p_vout->p_sys->p_display, p_vout->p_sys->window,
733                   StructureNotifyMask | KeyPressMask |
734                   ButtonPressMask | ButtonReleaseMask | 
735                   PointerMotionMask );
736
737     /* If the cursor was formerly blank than blank it again */
738     if( !p_vout->p_sys->b_mouse_pointer_visible )
739     {
740         ToggleCursor( p_vout );
741         ToggleCursor( p_vout );
742     }
743
744     XSync( p_vout->p_sys->p_display, False );
745
746     /* At this stage, the window is open, displayed, and ready to
747      * receive data */
748
749     return( 0 );
750 }
751
752 /*****************************************************************************
753  * DestroyWindow: destroy the window
754  *****************************************************************************
755  *
756  *****************************************************************************/
757 static void DestroyWindow( vout_thread_t *p_vout )
758 {
759     XSync( p_vout->p_sys->p_display, False );
760
761     XUnmapWindow( p_vout->p_sys->p_display, p_vout->p_sys->window );
762     XFreeGC( p_vout->p_sys->p_display, p_vout->p_sys->gc );
763     XDestroyWindow( p_vout->p_sys->p_display, p_vout->p_sys->window );
764 }
765
766 /*****************************************************************************
767  * NewPicture: allocate a picture
768  *****************************************************************************
769  * Returns 0 on success, -1 otherwise
770  *****************************************************************************/
771 static int NewPicture( vout_thread_t *p_vout, picture_t *p_pic )
772 {
773     /* We know the chroma, allocate a buffer which will be used
774      * directly by the decoder */
775     switch( p_vout->output.i_chroma )
776     {
777         /* XXX ?? */
778
779         default:
780             /* Unknown chroma, tell the guy to get lost */
781             msg_Err( p_vout, "never heard of chroma 0x%.8x (%4.4s)",
782                      p_vout->output.i_chroma, (char*)&p_vout->output.i_chroma );
783             p_pic->i_planes = 0;
784             return -1;
785     }
786
787     return 0;
788 }
789
790 /*****************************************************************************
791  * FreePicture: destroy a picture allocated with NewPicture
792  *****************************************************************************
793  * Destroy XImage AND associated data. If using Shm, detach shared memory
794  * segment from server and process, then free it. The XDestroyImage manpage
795  * says that both the image structure _and_ the data pointed to by the
796  * image structure are freed, so no need to free p_image->data.
797  *****************************************************************************/
798 static void FreePicture( vout_thread_t *p_vout, picture_t *p_pic )
799 {
800     XSync( p_vout->p_sys->p_display, False );
801 }
802
803 /*****************************************************************************
804  * ToggleFullScreen: Enable or disable full screen mode
805  *****************************************************************************
806  * This function will switch between fullscreen and window mode.
807  *
808  *****************************************************************************/
809 static void ToggleFullScreen ( vout_thread_t *p_vout )
810 {
811     Atom prop;
812     mwmhints_t mwmhints;
813     int i_xpos, i_ypos, i_width, i_height;
814     XEvent xevent;
815     XSetWindowAttributes attributes;
816
817     p_vout->b_fullscreen = !p_vout->b_fullscreen;
818
819     if( p_vout->b_fullscreen )
820     {
821         Window next_parent, parent, *p_dummy, dummy1;
822         unsigned int dummy2, dummy3;
823
824         msg_Dbg( p_vout, "entering fullscreen mode" );
825
826         /* Only check the fullscreen method when we actually go fullscreen,
827          * because to go back to window mode we need to know in which
828          * fullscreen mode we where */
829         p_vout->p_sys->b_altfullscreen = config_GetInt( p_vout,
830                                                         "xmga-altfullscreen" );
831
832         /* Save current window coordinates so they can be restored when
833          * we exit from fullscreen mode. This is the tricky part because
834          * this heavily depends on the behaviour of the window manager.
835          * When you use XMoveWindow some window managers will adjust the top
836          * of the window to the coordinates you gave, but others will instead
837          * adjust the top of the client area to the coordinates
838          * (don't forget windows have decorations). */
839
840         /* First, get the position and size of the client area */
841         XGetGeometry( p_vout->p_sys->p_display,
842                       p_vout->p_sys->window,
843                       &dummy1,
844                       &dummy2,
845                       &dummy3,
846                       &p_vout->p_sys->i_width_backup_2,
847                       &p_vout->p_sys->i_height_backup_2,
848                       &dummy2, &dummy3 );
849         XTranslateCoordinates( p_vout->p_sys->p_display,
850                                p_vout->p_sys->window,
851                                DefaultRootWindow( p_vout->p_sys->p_display ),
852                                0,
853                                0,
854                                &p_vout->p_sys->i_xpos_backup_2,
855                                &p_vout->p_sys->i_ypos_backup_2,
856                                &dummy1 );
857
858         /* Then try to get the position and size of the whole window */
859
860         /* find the real parent of our window (created by the window manager),
861          * the one which is a direct child of the root window */
862         next_parent = parent = p_vout->p_sys->window;
863         while( next_parent != DefaultRootWindow( p_vout->p_sys->p_display ) )
864         {
865             parent = next_parent;
866             XQueryTree( p_vout->p_sys->p_display,
867                         parent,
868                         &dummy1,
869                         &next_parent,
870                         &p_dummy,
871                         &dummy2 );
872             XFree((void *)p_dummy);
873         }
874
875         XGetGeometry( p_vout->p_sys->p_display,
876                       p_vout->p_sys->window,
877                       &dummy1,
878                       &dummy2,
879                       &dummy3,
880                       &p_vout->p_sys->i_width_backup,
881                       &p_vout->p_sys->i_height_backup,
882                       &dummy2, &dummy3 );
883
884         XTranslateCoordinates( p_vout->p_sys->p_display,
885                                parent,
886                                DefaultRootWindow( p_vout->p_sys->p_display ),
887                                0,
888                                0,
889                                &p_vout->p_sys->i_xpos_backup,
890                                &p_vout->p_sys->i_ypos_backup,
891                                &dummy1 );
892
893         /* fullscreen window size and position */
894         i_xpos = 0;
895         i_ypos = 0;
896         i_width = DisplayWidth( p_vout->p_sys->p_display,
897                                 p_vout->p_sys->i_screen );
898         i_height = DisplayHeight( p_vout->p_sys->p_display,
899                                   p_vout->p_sys->i_screen );
900
901     }
902     else
903     {
904         msg_Dbg( p_vout, "leaving fullscreen mode" );
905
906         i_xpos = p_vout->p_sys->i_xpos_backup;
907         i_ypos = p_vout->p_sys->i_ypos_backup;
908         i_width = p_vout->p_sys->i_width_backup;
909         i_height = p_vout->p_sys->i_height_backup;
910     }
911
912     /* To my knowledge there are two ways to create a borderless window.
913      * There's the generic way which is to tell x to bypass the window manager,
914      * but this creates problems with the focus of other applications.
915      * The other way is to use the motif property "_MOTIF_WM_HINTS" which
916      * luckily seems to be supported by most window managers.
917      */
918     if( !p_vout->p_sys->b_altfullscreen )
919     {
920         mwmhints.flags = MWM_HINTS_DECORATIONS;
921         mwmhints.decorations = !p_vout->b_fullscreen;
922
923         prop = XInternAtom( p_vout->p_sys->p_display, "_MOTIF_WM_HINTS",
924                             False );
925         XChangeProperty( p_vout->p_sys->p_display, p_vout->p_sys->window,
926                          prop, prop, 32, PropModeReplace,
927                          (unsigned char *)&mwmhints,
928                          PROP_MWM_HINTS_ELEMENTS );
929     }
930     else
931     {
932         /* brute force way to remove decorations */
933         attributes.override_redirect = p_vout->b_fullscreen;
934         XChangeWindowAttributes( p_vout->p_sys->p_display,
935                                  p_vout->p_sys->window,
936                                  CWOverrideRedirect,
937                                  &attributes);
938     }
939
940     /* We need to unmap and remap the window if we want the window 
941      * manager to take our changes into effect */
942     XUnmapWindow( p_vout->p_sys->p_display, p_vout->p_sys->window);
943
944     XWindowEvent( p_vout->p_sys->p_display, p_vout->p_sys->window,
945                   StructureNotifyMask, &xevent );
946     while( xevent.type != UnmapNotify )
947         XWindowEvent( p_vout->p_sys->p_display, p_vout->p_sys->window,
948                       StructureNotifyMask, &xevent );
949
950     XMapRaised( p_vout->p_sys->p_display, p_vout->p_sys->window);
951
952     while( xevent.type != MapNotify )
953         XWindowEvent( p_vout->p_sys->p_display, p_vout->p_sys->window,
954                       StructureNotifyMask, &xevent );
955
956     XMoveResizeWindow( p_vout->p_sys->p_display,
957                        p_vout->p_sys->window,
958                        i_xpos,
959                        i_ypos,
960                        i_width,
961                        i_height );
962
963     /* Purge all ConfigureNotify events, this is needed to fix a bug where we
964      * would lose the original size of the window */
965     while( xevent.type != ConfigureNotify )
966         XWindowEvent( p_vout->p_sys->p_display, p_vout->p_sys->window,
967                       StructureNotifyMask, &xevent );
968     while( XCheckWindowEvent( p_vout->p_sys->p_display, p_vout->p_sys->window,
969                               StructureNotifyMask, &xevent ) );
970
971
972     /* We need to check that the window was really restored where we wanted */
973     if( !p_vout->b_fullscreen )
974     {
975         Window dummy1;
976         unsigned int dummy2, dummy3, dummy4, dummy5;
977
978         /* Check the position */
979         XTranslateCoordinates( p_vout->p_sys->p_display,
980                                p_vout->p_sys->window,
981                                DefaultRootWindow( p_vout->p_sys->p_display ),
982                                0,
983                                0,
984                                &dummy2,
985                                &dummy3,
986                                &dummy1 );
987         if( dummy2 != p_vout->p_sys->i_xpos_backup_2 ||
988             dummy3 != p_vout->p_sys->i_ypos_backup_2 )
989         {
990             /* Ok it didn't work... second try */
991
992             XMoveWindow( p_vout->p_sys->p_display,
993                          p_vout->p_sys->window,
994                          p_vout->p_sys->i_xpos_backup_2,
995                          p_vout->p_sys->i_ypos_backup_2 );
996             
997             /* Purge all ConfigureNotify events, this is needed to fix a bug
998              * where we would lose the original size of the window */
999             XWindowEvent( p_vout->p_sys->p_display, p_vout->p_sys->window,
1000                           StructureNotifyMask, &xevent );
1001             while( xevent.type != ConfigureNotify )
1002                 XWindowEvent( p_vout->p_sys->p_display, p_vout->p_sys->window,
1003                               StructureNotifyMask, &xevent );
1004             while( XCheckWindowEvent( p_vout->p_sys->p_display,
1005                                       p_vout->p_sys->window,
1006                                       StructureNotifyMask, &xevent ) );
1007         }
1008
1009         /* Check the size */
1010         XGetGeometry( p_vout->p_sys->p_display,
1011                       p_vout->p_sys->window,
1012                       &dummy1,
1013                       &dummy2,
1014                       &dummy3,
1015                       &dummy4,
1016                       &dummy5,
1017                       &dummy2, &dummy3 );
1018
1019         if( dummy4 != p_vout->p_sys->i_width_backup_2 ||
1020             dummy5 != p_vout->p_sys->i_height_backup_2 )
1021         {
1022             /* Ok it didn't work... third try */
1023
1024             XResizeWindow( p_vout->p_sys->p_display,
1025                          p_vout->p_sys->window,
1026                          p_vout->p_sys->i_width_backup_2,
1027                          p_vout->p_sys->i_height_backup_2 );
1028             
1029             /* Purge all ConfigureNotify events, this is needed to fix a bug
1030              * where we would lose the original size of the window */
1031             XWindowEvent( p_vout->p_sys->p_display, p_vout->p_sys->window,
1032                           StructureNotifyMask, &xevent );
1033             while( xevent.type != ConfigureNotify )
1034                 XWindowEvent( p_vout->p_sys->p_display, p_vout->p_sys->window,
1035                               StructureNotifyMask, &xevent );
1036             while( XCheckWindowEvent( p_vout->p_sys->p_display,
1037                                       p_vout->p_sys->window,
1038                                       StructureNotifyMask, &xevent ) );
1039         }
1040     }
1041
1042     if( p_vout->p_sys->b_altfullscreen )
1043         XSetInputFocus(p_vout->p_sys->p_display,
1044                        p_vout->p_sys->window,
1045                        RevertToParent,
1046                        CurrentTime);
1047
1048     /* signal that the size needs to be updated */
1049     p_vout->p_sys->i_width = i_width;
1050     p_vout->p_sys->i_height = i_height;
1051     p_vout->i_changes |= VOUT_SIZE_CHANGE;
1052
1053 }
1054
1055 /*****************************************************************************
1056  * EnableXScreenSaver: enable screen saver
1057  *****************************************************************************
1058  * This function enables the screen saver on a display after it has been
1059  * disabled by XDisableScreenSaver.
1060  * FIXME: what happens if multiple vlc sessions are running at the same
1061  *        time ???
1062  *****************************************************************************/
1063 static void EnableXScreenSaver( vout_thread_t *p_vout )
1064 {
1065     int dummy;
1066
1067     XSetScreenSaver( p_vout->p_sys->p_display, p_vout->p_sys->i_ss_timeout,
1068                      p_vout->p_sys->i_ss_interval,
1069                      p_vout->p_sys->i_ss_blanking,
1070                      p_vout->p_sys->i_ss_exposure );
1071
1072     /* Restore DPMS settings */
1073     if( DPMSQueryExtension( p_vout->p_sys->p_display, &dummy, &dummy ) )
1074     {
1075         if( p_vout->p_sys->b_ss_dpms )
1076         {
1077             DPMSEnable( p_vout->p_sys->p_display );
1078         }
1079     }
1080 }
1081
1082 /*****************************************************************************
1083  * DisableXScreenSaver: disable screen saver
1084  *****************************************************************************
1085  * See XEnableXScreenSaver
1086  *****************************************************************************/
1087 static void DisableXScreenSaver( vout_thread_t *p_vout )
1088 {
1089     int dummy;
1090
1091     /* Save screen saver informations */
1092     XGetScreenSaver( p_vout->p_sys->p_display, &p_vout->p_sys->i_ss_timeout,
1093                      &p_vout->p_sys->i_ss_interval,
1094                      &p_vout->p_sys->i_ss_blanking,
1095                      &p_vout->p_sys->i_ss_exposure );
1096
1097     /* Disable screen saver */
1098     XSetScreenSaver( p_vout->p_sys->p_display, 0,
1099                      p_vout->p_sys->i_ss_interval,
1100                      p_vout->p_sys->i_ss_blanking,
1101                      p_vout->p_sys->i_ss_exposure );
1102
1103     /* Disable DPMS */
1104     if( DPMSQueryExtension( p_vout->p_sys->p_display, &dummy, &dummy ) )
1105     {
1106         CARD16 dummy;
1107         /* Save DPMS current state */
1108         DPMSInfo( p_vout->p_sys->p_display, &dummy,
1109                   &p_vout->p_sys->b_ss_dpms );
1110         DPMSDisable( p_vout->p_sys->p_display );
1111    }
1112 }
1113
1114 /*****************************************************************************
1115  * CreateCursor: create a blank mouse pointer
1116  *****************************************************************************/
1117 static void CreateCursor( vout_thread_t *p_vout )
1118 {
1119     XColor cursor_color;
1120
1121     p_vout->p_sys->cursor_pixmap =
1122         XCreatePixmap( p_vout->p_sys->p_display,
1123                        DefaultRootWindow( p_vout->p_sys->p_display ),
1124                        1, 1, 1 );
1125
1126     XParseColor( p_vout->p_sys->p_display,
1127                  XCreateColormap( p_vout->p_sys->p_display,
1128                                   DefaultRootWindow(
1129                                                     p_vout->p_sys->p_display ),
1130                                   DefaultVisual(
1131                                                 p_vout->p_sys->p_display,
1132                                                 p_vout->p_sys->i_screen ),
1133                                   AllocNone ),
1134                  "black", &cursor_color );
1135
1136     p_vout->p_sys->blank_cursor =
1137         XCreatePixmapCursor( p_vout->p_sys->p_display,
1138                              p_vout->p_sys->cursor_pixmap,
1139                              p_vout->p_sys->cursor_pixmap,
1140                              &cursor_color, &cursor_color, 1, 1 );
1141 }
1142
1143 /*****************************************************************************
1144  * DestroyCursor: destroy the blank mouse pointer
1145  *****************************************************************************/
1146 static void DestroyCursor( vout_thread_t *p_vout )
1147 {
1148     XFreePixmap( p_vout->p_sys->p_display, p_vout->p_sys->cursor_pixmap );
1149 }
1150
1151 /*****************************************************************************
1152  * ToggleCursor: hide or show the mouse pointer
1153  *****************************************************************************
1154  * This function hides the X pointer if it is visible by setting the pointer
1155  * sprite to a blank one. To show it again, we disable the sprite.
1156  *****************************************************************************/
1157 static void ToggleCursor( vout_thread_t *p_vout )
1158 {
1159     if( p_vout->p_sys->b_mouse_pointer_visible )
1160     {
1161         XDefineCursor( p_vout->p_sys->p_display,
1162                        p_vout->p_sys->window,
1163                        p_vout->p_sys->blank_cursor );
1164         p_vout->p_sys->b_mouse_pointer_visible = 0;
1165     }
1166     else
1167     {
1168         XUndefineCursor( p_vout->p_sys->p_display, p_vout->p_sys->window );
1169         p_vout->p_sys->b_mouse_pointer_visible = 1;
1170     }
1171 }
1172