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