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