d74f937b74e7dad0c8ac29df0d4328e27cdc8933
[vlc.git] / modules / video_output / sdl.c
1 /*****************************************************************************
2  * sdl.c: SDL video output display method
3  *****************************************************************************
4  * Copyright (C) 1998-2001 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Samuel Hocevar <sam@zoy.org>
8  *          Pierre Baillet <oct@zoy.org>
9  *          Arnaud de Bossoreille de Ribou <bozo@via.ecp.fr>
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., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
24  *****************************************************************************/
25
26 /*****************************************************************************
27  * Preamble
28  *****************************************************************************/
29 #include <errno.h>                                                 /* ENOMEM */
30
31 #include <vlc/vlc.h>
32 #include <vlc_interface.h>
33 #include <vlc_playlist.h>
34 #include <vlc_vout.h>
35 #include <vlc_keys.h>
36 //#include <vlc_aout.h>
37
38 #include <sys/types.h>
39 #ifndef WIN32
40 #   include <netinet/in.h>                            /* BSD: struct in_addr */
41 #endif
42
43 #include SDL_INCLUDE_FILE
44
45 /* SDL is not able to crop overlays - so use only 1 direct buffer */
46 #define SDL_MAX_DIRECTBUFFERS 1
47 #define SDL_DEFAULT_BPP 16
48
49 /*****************************************************************************
50  * vout_sys_t: video output SDL method descriptor
51  *****************************************************************************
52  * This structure is part of the video output thread descriptor.
53  * It describes the SDL specific properties of an output thread.
54  *****************************************************************************/
55 struct vout_sys_t
56 {
57     SDL_Surface *   p_display;                             /* display device */
58
59     int i_width;
60     int i_height;
61
62     /* For YUV output */
63     SDL_Overlay * p_overlay;   /* An overlay we keep to grab the XVideo port */
64
65     /* For RGB output */
66     int i_surfaces;
67
68     vlc_bool_t  b_cursor;
69     vlc_bool_t  b_cursor_autohidden;
70     mtime_t     i_lastmoved;
71     mtime_t     i_lastpressed;                        /* to track dbl-clicks */
72 };
73
74 /*****************************************************************************
75  * picture_sys_t: direct buffer method descriptor
76  *****************************************************************************
77  * This structure is part of the picture descriptor, it describes the
78  * SDL specific properties of a direct buffer.
79  *****************************************************************************/
80 struct picture_sys_t
81 {
82     SDL_Overlay *p_overlay;
83 };
84
85 /*****************************************************************************
86  * Local prototypes
87  *****************************************************************************/
88 static int  Open      ( vlc_object_t * );
89 static void Close     ( vlc_object_t * );
90 static int  Init      ( vout_thread_t * );
91 static void End       ( vout_thread_t * );
92 static int  Manage    ( vout_thread_t * );
93 static void Display   ( vout_thread_t *, picture_t * );
94
95 static int  OpenDisplay     ( vout_thread_t * );
96 static void CloseDisplay    ( vout_thread_t * );
97 static int  NewPicture      ( vout_thread_t *, picture_t * );
98 static void SetPalette      ( vout_thread_t *,
99                               uint16_t *, uint16_t *, uint16_t * );
100
101 static int ConvertKey( SDLKey );
102
103
104 #define CHROMA_TEXT N_("SDL chroma format")
105 #define CHROMA_LONGTEXT N_( \
106     "Force the SDL renderer to use a specific chroma format instead of " \
107     "trying to improve performances by using the most efficient one.")
108
109 /*****************************************************************************
110  * Module descriptor
111  *****************************************************************************/
112 vlc_module_begin();
113     set_shortname( "SDL" );
114     set_category( CAT_VIDEO );
115     set_subcategory( SUBCAT_VIDEO_VOUT );
116     set_description( _("Simple DirectMedia Layer video output") );
117     set_capability( "video output", 60 );
118     add_shortcut( "sdl" );
119     add_string( "sdl-chroma", NULL, NULL, CHROMA_TEXT, CHROMA_LONGTEXT, VLC_TRUE );
120     set_callbacks( Open, Close );
121 #if defined( __i386__ ) || defined( __x86_64__ )
122     /* On i386, SDL is linked against svgalib */
123     linked_with_a_crap_library_which_uses_atexit();
124 #endif
125 vlc_module_end();
126
127 /*****************************************************************************
128  * OpenVideo: allocate SDL video thread output method
129  *****************************************************************************
130  * This function allocate and initialize a SDL vout method. It uses some of the
131  * vout properties to choose the correct mode, and change them according to the
132  * mode actually used.
133  *****************************************************************************/
134 static int Open ( vlc_object_t *p_this )
135 {
136     vout_thread_t * p_vout = (vout_thread_t *)p_this;
137     /* XXX: check for conflicts with the SDL audio output */
138     vlc_mutex_t *lock = var_AcquireMutex( "sdl" );
139
140 #ifdef HAVE_SETENV
141     char *psz_method;
142 #endif
143
144     if( lock == NULL )
145         return VLC_ENOMEM;
146
147     p_vout->p_sys = malloc( sizeof( vout_sys_t ) );
148     if( p_vout->p_sys == NULL )
149     {
150         vlc_mutex_unlock( lock );
151         return VLC_ENOMEM;
152     }
153
154     if( SDL_WasInit( SDL_INIT_VIDEO ) != 0 )
155     {
156         vlc_mutex_unlock( lock );
157         free( p_vout->p_sys );
158         return VLC_EGENERIC;
159     }
160
161     /* Allocate structure */
162
163     p_vout->pf_init = Init;
164     p_vout->pf_end = End;
165     p_vout->pf_manage = Manage;
166     p_vout->pf_render = NULL;
167     p_vout->pf_display = Display;
168
169 #ifdef HAVE_SETENV
170     psz_method = config_GetPsz( p_vout, "vout" );
171     if( psz_method )
172     {
173         while( *psz_method && *psz_method != ':' )
174         {
175             psz_method++;
176         }
177
178         if( *psz_method )
179         {
180             setenv( "SDL_VIDEODRIVER", psz_method + 1, 1 );
181         }
182     }
183 #endif
184
185     /* Initialize library */
186     if( SDL_Init( SDL_INIT_VIDEO
187 #ifndef WIN32
188     /* Win32 SDL implementation doesn't support SDL_INIT_EVENTTHREAD yet*/
189                 | SDL_INIT_EVENTTHREAD
190 #endif
191 #ifdef DEBUG
192     /* In debug mode you may want vlc to dump a core instead of staying
193      * stuck */
194                 | SDL_INIT_NOPARACHUTE
195 #endif
196                 ) < 0 )
197     {
198         msg_Err( p_vout, "cannot initialize SDL (%s)", SDL_GetError() );
199         free( p_vout->p_sys );
200         vlc_mutex_unlock( lock );
201         return VLC_EGENERIC;
202     }
203
204     /* Translate keys into unicode */
205     SDL_EnableUNICODE(1);
206
207     vlc_mutex_unlock( lock );
208
209     p_vout->p_sys->b_cursor = 1;
210     p_vout->p_sys->b_cursor_autohidden = 0;
211     p_vout->p_sys->i_lastmoved = mdate();
212
213     if( OpenDisplay( p_vout ) )
214     {
215         msg_Err( p_vout, "cannot set up SDL (%s)", SDL_GetError() );
216         SDL_QuitSubSystem( SDL_INIT_VIDEO );
217         free( p_vout->p_sys );
218         return VLC_EGENERIC;
219     }
220
221     return VLC_SUCCESS;
222 }
223
224 /*****************************************************************************
225  * Init: initialize SDL video thread output method
226  *****************************************************************************
227  * This function initialize the SDL display device.
228  *****************************************************************************/
229 static int Init( vout_thread_t *p_vout )
230 {
231     int i_index;
232     picture_t *p_pic;
233
234     p_vout->p_sys->i_surfaces = 0;
235
236     I_OUTPUTPICTURES = 0;
237
238     /* Initialize the output structure */
239     if( p_vout->p_sys->p_overlay == NULL )
240     {
241         /* All we have is an RGB image with square pixels */
242         p_vout->output.i_width  = p_vout->p_sys->i_width;
243         p_vout->output.i_height = p_vout->p_sys->i_height;
244         p_vout->output.i_aspect = p_vout->output.i_width
245                                    * VOUT_ASPECT_FACTOR
246                                    / p_vout->output.i_height;
247     }
248     else
249     {
250         /* We may need to convert the chroma, but at least we keep the
251          * aspect ratio */
252         p_vout->output.i_width  = p_vout->render.i_width;
253         p_vout->output.i_height = p_vout->render.i_height;
254         p_vout->output.i_aspect = p_vout->render.i_aspect;
255     }
256
257     /* Try to initialize SDL_MAX_DIRECTBUFFERS direct buffers */
258     while( I_OUTPUTPICTURES < SDL_MAX_DIRECTBUFFERS )
259     {
260         p_pic = NULL;
261
262         /* Find an empty picture slot */
263         for( i_index = 0 ; i_index < VOUT_MAX_PICTURES ; i_index++ )
264         {
265             if( p_vout->p_picture[ i_index ].i_status == FREE_PICTURE )
266             {
267                 p_pic = p_vout->p_picture + i_index;
268                 break;
269             }
270         }
271
272         /* Allocate the picture if we found one */
273         if( p_pic == NULL || NewPicture( p_vout, p_pic ) )
274         {
275             break;
276         }
277
278         p_pic->i_status = DESTROYED_PICTURE;
279         p_pic->i_type   = DIRECT_PICTURE;
280
281         PP_OUTPUTPICTURE[ I_OUTPUTPICTURES ] = p_pic;
282
283         I_OUTPUTPICTURES++;
284     }
285
286     return VLC_SUCCESS;
287 }
288
289 /*****************************************************************************
290  * End: terminate Sys video thread output method
291  *****************************************************************************
292  * Terminate an output method created by OpenVideo
293  *****************************************************************************/
294 static void End( vout_thread_t *p_vout )
295 {
296     int i_index;
297
298     /* Free the output buffers we allocated */
299     for( i_index = I_OUTPUTPICTURES ; i_index ; )
300     {
301         i_index--;
302         if( p_vout->p_sys->p_overlay == NULL )
303         {
304             /* RGB picture */
305         }
306         else
307         {
308             SDL_UnlockYUVOverlay(
309                     PP_OUTPUTPICTURE[ i_index ]->p_sys->p_overlay );
310             SDL_FreeYUVOverlay(
311                     PP_OUTPUTPICTURE[ i_index ]->p_sys->p_overlay );
312         }
313         free( PP_OUTPUTPICTURE[ i_index ]->p_sys );
314     }
315 }
316
317 /*****************************************************************************
318  * CloseVideo: destroy Sys video thread output method
319  *****************************************************************************
320  * Terminate an output method created by vout_SDLCreate
321  *****************************************************************************/
322 static void Close ( vlc_object_t *p_this )
323 {
324     vout_thread_t * p_vout = (vout_thread_t *)p_this;
325
326     CloseDisplay( p_vout );
327     SDL_QuitSubSystem( SDL_INIT_VIDEO );
328
329     free( p_vout->p_sys );
330 }
331
332 /*****************************************************************************
333  * Manage: handle Sys events
334  *****************************************************************************
335  * This function should be called regularly by video output thread. It returns
336  * a non null value if an error occurred.
337  *****************************************************************************/
338 static int Manage( vout_thread_t *p_vout )
339 {
340     SDL_Event event;                                            /* SDL event */
341     vlc_value_t val;
342     unsigned int i_width, i_height, i_x, i_y;
343
344     /* Process events */
345     while( SDL_PollEvent( &event ) )
346     {
347         switch( event.type )
348         {
349         case SDL_VIDEORESIZE:                          /* Resizing of window */
350             /* Update dimensions */
351             p_vout->i_changes |= VOUT_SIZE_CHANGE;
352             p_vout->i_window_width = p_vout->p_sys->i_width = event.resize.w;
353             p_vout->i_window_height = p_vout->p_sys->i_height = event.resize.h;
354             break;
355
356         /* Mouse move */
357         case SDL_MOUSEMOTION:
358             vout_PlacePicture( p_vout, p_vout->p_sys->i_width,
359                                p_vout->p_sys->i_height,
360                                &i_x, &i_y, &i_width, &i_height );
361
362             val.i_int = ( event.motion.x - i_x )
363                          * p_vout->render.i_width / i_width;
364             var_Set( p_vout, "mouse-x", val );
365             val.i_int = ( event.motion.y - i_y )
366                          * p_vout->render.i_height / i_height;
367             var_Set( p_vout, "mouse-y", val );
368
369             val.b_bool = VLC_TRUE;
370             var_Set( p_vout, "mouse-moved", val );
371
372             if( p_vout->p_sys->b_cursor &&
373                 (abs(event.motion.xrel) > 2 || abs(event.motion.yrel) > 2) )
374             {
375                 if( p_vout->p_sys->b_cursor_autohidden )
376                 {
377                     p_vout->p_sys->b_cursor_autohidden = 0;
378                     SDL_ShowCursor( 1 );
379                 }
380                 else
381                 {
382                     p_vout->p_sys->i_lastmoved = mdate();
383                 }
384             }
385             break;
386
387         /* Mouse button pressed */
388         case SDL_MOUSEBUTTONUP:
389             switch( event.button.button )
390             {
391             case SDL_BUTTON_LEFT:
392                 var_Get( p_vout, "mouse-button-down", &val );
393                 val.i_int &= ~1;
394                 var_Set( p_vout, "mouse-button-down", val );
395
396                 val.b_bool = VLC_TRUE;
397                 var_Set( p_vout, "mouse-clicked", val );
398                 break;
399
400             case SDL_BUTTON_MIDDLE:
401                 {
402                     playlist_t *p_playlist;
403
404                     var_Get( p_vout, "mouse-button-down", &val );
405                     val.i_int &= ~2;
406                     var_Set( p_vout, "mouse-button-down", val );
407
408                     p_playlist = vlc_object_find( p_vout, VLC_OBJECT_PLAYLIST,
409                                               FIND_ANYWHERE );
410                     if( p_playlist != NULL )
411                     {
412                         vlc_value_t val;
413                         var_Get( p_playlist, "intf-show", &val );
414                         val.b_bool = !val.b_bool;
415                         var_Set( p_playlist, "intf-show", val );
416                         vlc_object_release( p_playlist );
417                     }
418                 }
419                 break;
420
421             case SDL_BUTTON_RIGHT:
422                 {
423                     intf_thread_t *p_intf;
424                     playlist_t *p_playlist;
425
426                     var_Get( p_vout, "mouse-button-down", &val );
427                     val.i_int &= ~4;
428                     var_Set( p_vout, "mous-button-down", val );
429                     p_intf = vlc_object_find( p_vout, VLC_OBJECT_INTF,
430                                                       FIND_ANYWHERE );
431                     if( p_intf )
432                     {
433                         p_intf->b_menu_change = 1;
434                         vlc_object_release( p_intf );
435                     }
436
437                     p_playlist = vlc_object_find( p_vout, VLC_OBJECT_PLAYLIST,
438                                                 FIND_ANYWHERE );
439
440                     if( p_playlist != NULL )
441                     {
442                         vlc_value_t val;
443                         val.b_bool = VLC_TRUE;
444                         var_Set( p_playlist, "intf-popupmenu", val );
445                         vlc_object_release( p_playlist );
446                     }
447                 }
448                 break;
449             }
450             break;
451
452         /* Mouse button released */
453         case SDL_MOUSEBUTTONDOWN:
454             switch( event.button.button )
455             {
456             case SDL_BUTTON_LEFT:
457                 var_Get( p_vout, "mouse-button-down", &val );
458                 val.i_int |= 1;
459                 var_Set( p_vout, "mouse-button-down", val );
460
461                 /* detect double-clicks */
462                 if( ( mdate() - p_vout->p_sys->i_lastpressed ) < 300000 )
463                     p_vout->i_changes |= VOUT_FULLSCREEN_CHANGE;
464
465                 p_vout->p_sys->i_lastpressed = mdate();
466                 break;
467
468             case SDL_BUTTON_MIDDLE:
469                 var_Get( p_vout, "mouse-button-down", &val );
470                 val.i_int |= 2;
471                 var_Set( p_vout, "mouse-button-down", val );
472                 break;
473
474             case SDL_BUTTON_RIGHT:
475                 var_Get( p_vout, "mouse-button-down", &val );
476                 val.i_int |= 4;
477                 var_Set( p_vout, "mouse-button-down", val );
478                 break;
479             }
480             break;
481
482         /* Quit event (close the window) */
483         case SDL_QUIT:
484             {
485                 playlist_t *p_playlist = (playlist_t *)vlc_object_find( p_vout, VLC_OBJECT_PLAYLIST, FIND_ANYWHERE );
486                 if( p_playlist != NULL )
487                 {
488                     playlist_Stop( p_playlist );
489                     vlc_object_release( p_playlist );
490                 }
491             }
492             break;
493
494         /* Key pressed */
495         case SDL_KEYDOWN:
496             val.i_int = ConvertKey( event.key.keysym.sym );
497
498             if( !val.i_int )
499             {
500                 if( ( event.key.keysym.unicode & 0xff80 ) == 0 )
501                 {
502                     val.i_int = event.key.keysym.unicode & 0x7f;
503                 }
504             }
505
506             if( val.i_int )
507             {
508                 if( ( event.key.keysym.mod & KMOD_SHIFT ) )
509                 {
510                     val.i_int |= KEY_MODIFIER_SHIFT;
511                 }
512                 if( ( event.key.keysym.mod & KMOD_CTRL ) )
513                 {
514                     val.i_int |= KEY_MODIFIER_CTRL;
515                 }
516                 if( ( event.key.keysym.mod & KMOD_ALT ) )
517                 {
518                     val.i_int |= KEY_MODIFIER_ALT;
519                 }
520                 var_Set( p_vout->p_libvlc, "key-pressed", val );
521             }
522
523         default:
524             break;
525         }
526     }
527
528     /* Fullscreen change */
529     if( p_vout->i_changes & VOUT_FULLSCREEN_CHANGE )
530     {
531         vlc_value_t val_fs;
532
533         /* Update the object variable and trigger callback */
534         val_fs.b_bool = !p_vout->b_fullscreen;
535         p_vout->b_fullscreen = !p_vout->b_fullscreen;
536         var_Set( p_vout, "fullscreen", val_fs );
537
538         /*TODO: add the always on top code !*/
539
540         p_vout->p_sys->b_cursor_autohidden = 0;
541         SDL_ShowCursor( p_vout->p_sys->b_cursor &&
542                         ! p_vout->p_sys->b_cursor_autohidden );
543
544         p_vout->i_changes &= ~VOUT_FULLSCREEN_CHANGE;
545         p_vout->i_changes |= VOUT_SIZE_CHANGE;
546     }
547
548     /* Size change */
549     if( p_vout->i_changes & VOUT_SIZE_CHANGE )
550     {
551         msg_Dbg( p_vout, "video display resized (%dx%d)",
552                  p_vout->p_sys->i_width, p_vout->p_sys->i_height );
553
554         CloseDisplay( p_vout );
555         OpenDisplay( p_vout );
556
557         /* We don't need to signal the vout thread about the size change if
558          * we can handle rescaling ourselves */
559         if( p_vout->p_sys->p_overlay != NULL )
560             p_vout->i_changes &= ~VOUT_SIZE_CHANGE;
561
562     }
563
564     /* Pointer change */
565     if( ! p_vout->p_sys->b_cursor_autohidden &&
566         ( mdate() - p_vout->p_sys->i_lastmoved > 2000000 ) )
567     {
568         /* Hide the mouse automatically */
569         p_vout->p_sys->b_cursor_autohidden = 1;
570         SDL_ShowCursor( 0 );
571     }
572
573     return VLC_SUCCESS;
574 }
575
576 /*****************************************************************************
577  * Key events handling
578  *****************************************************************************/
579 static struct
580 {
581     SDLKey sdl_key;
582     int i_vlckey;
583 } sdlkeys_to_vlckeys[] =
584 {
585     { SDLK_F1,  KEY_F1 },
586     { SDLK_F2,  KEY_F2 },
587     { SDLK_F3,  KEY_F3 },
588     { SDLK_F4,  KEY_F4 },
589     { SDLK_F5,  KEY_F5 },
590     { SDLK_F6,  KEY_F6 },
591     { SDLK_F7,  KEY_F7 },
592     { SDLK_F8,  KEY_F8 },
593     { SDLK_F9,  KEY_F9 },
594     { SDLK_F10, KEY_F10 },
595     { SDLK_F11, KEY_F11 },
596     { SDLK_F12, KEY_F12 },
597
598     { SDLK_RETURN, KEY_ENTER },
599     { SDLK_KP_ENTER, KEY_ENTER },
600     { SDLK_SPACE, KEY_SPACE },
601     { SDLK_ESCAPE, KEY_ESC },
602
603     { SDLK_MENU, KEY_MENU },
604     { SDLK_LEFT, KEY_LEFT },
605     { SDLK_RIGHT, KEY_RIGHT },
606     { SDLK_UP, KEY_UP },
607     { SDLK_DOWN, KEY_DOWN },
608
609     { SDLK_HOME, KEY_HOME },
610     { SDLK_END, KEY_END },
611     { SDLK_PAGEUP, KEY_PAGEUP },
612     { SDLK_PAGEDOWN,  KEY_PAGEDOWN },
613
614     { SDLK_INSERT, KEY_INSERT },
615     { SDLK_DELETE, KEY_DELETE },
616     /*TODO: find a equivalent for SDL 
617     { , KEY_MEDIA_NEXT_TRACK }
618     { , KEY_MEDIA_PREV_TRACK }
619     { , KEY_VOLUME_MUTE }
620     { , KEY_VOLUME_DOWN }
621     { , KEY_VOLUME_UP }
622     { , KEY_MEDIA_PLAY_PAUSE }
623     { , KEY_MEDIA_PLAY_PAUSE }*/
624
625     { 0, 0 }
626 };
627
628 static int ConvertKey( SDLKey sdl_key )
629 {
630     int i;
631     for( i=0; sdlkeys_to_vlckeys[i].sdl_key != 0; i++ )
632     {
633         if( sdlkeys_to_vlckeys[i].sdl_key == sdl_key )
634         {
635             return sdlkeys_to_vlckeys[i].i_vlckey;
636         }
637     }
638     return 0;
639 }
640
641
642 /*****************************************************************************
643  * Display: displays previously rendered output
644  *****************************************************************************
645  * This function sends the currently rendered image to the display.
646  *****************************************************************************/
647 static void Display( vout_thread_t *p_vout, picture_t *p_pic )
648 {
649     unsigned int x, y, w, h;
650     SDL_Rect disp;
651
652     vout_PlacePicture( p_vout, p_vout->p_sys->i_width, p_vout->p_sys->i_height,
653                        &x, &y, &w, &h );
654     disp.x = x;
655     disp.y = y;
656     disp.w = w;
657     disp.h = h;
658
659     if( p_vout->p_sys->p_overlay == NULL )
660     {
661         /* RGB picture */
662         SDL_Flip( p_vout->p_sys->p_display );
663     }
664     else
665     {
666         /* Overlay picture */
667         SDL_UnlockYUVOverlay( p_pic->p_sys->p_overlay);
668         SDL_DisplayYUVOverlay( p_pic->p_sys->p_overlay , &disp );
669         SDL_LockYUVOverlay( p_pic->p_sys->p_overlay);
670     }
671 }
672
673 /* following functions are local */
674
675 /*****************************************************************************
676  * OpenDisplay: open and initialize SDL device
677  *****************************************************************************
678  * Open and initialize display according to preferences specified in the vout
679  * thread fields.
680  *****************************************************************************/
681 static int OpenDisplay( vout_thread_t *p_vout )
682 {
683     uint32_t i_flags;
684     int i_bpp;
685
686     /* SDL fucked up fourcc definitions on bigendian machines */
687     uint32_t i_sdl_chroma;
688     char *psz_chroma = NULL;
689     uint32_t i_chroma = 0;
690
691     /* Set main window's size */
692     p_vout->p_sys->i_width = p_vout->b_fullscreen ? p_vout->output.i_width :
693                                                     p_vout->i_window_width;
694     p_vout->p_sys->i_height = p_vout->b_fullscreen ? p_vout->output.i_height :
695                                                      p_vout->i_window_height;
696
697     /* Initialize flags and cursor */
698     i_flags = SDL_ANYFORMAT | SDL_HWPALETTE | SDL_HWSURFACE | SDL_DOUBLEBUF;
699     i_flags |= p_vout->b_fullscreen ? SDL_FULLSCREEN : SDL_RESIZABLE;
700
701     i_bpp = SDL_VideoModeOK( p_vout->p_sys->i_width, p_vout->p_sys->i_height,
702                              SDL_DEFAULT_BPP, i_flags );
703     if( i_bpp == 0 )
704     {
705         msg_Err( p_vout, "no video mode available" );
706         return VLC_EGENERIC;
707     }
708
709     p_vout->p_sys->p_display = SDL_SetVideoMode( p_vout->p_sys->i_width,
710                                                  p_vout->p_sys->i_height,
711                                                  i_bpp, i_flags );
712
713     if( p_vout->p_sys->p_display == NULL )
714     {
715         msg_Err( p_vout, "cannot set video mode" );
716         return VLC_EGENERIC;
717     }
718
719     SDL_LockSurface( p_vout->p_sys->p_display );
720
721     if( ( psz_chroma = config_GetPsz( p_vout, "sdl-chroma" ) ) )
722     {
723         if( strlen( psz_chroma ) >= 4 )
724         {
725             memcpy(&i_chroma, psz_chroma, 4);
726             msg_Dbg( p_vout, "Forcing chroma to 0x%.8x (%4.4s)", i_chroma, (char*)&i_chroma );
727         }
728         else
729         {
730             free( psz_chroma );
731             psz_chroma = NULL;
732         }
733     }
734
735     /* Choose the chroma we will try first. */
736     do
737     {
738         if( !psz_chroma ) i_chroma = 0;
739         switch( i_chroma ? i_chroma : p_vout->render.i_chroma )
740         {
741             case VLC_FOURCC('Y','U','Y','2'):
742             case VLC_FOURCC('Y','U','N','V'):
743                 p_vout->output.i_chroma = VLC_FOURCC('Y','U','Y','2');
744                 i_sdl_chroma = SDL_YUY2_OVERLAY;
745                 break;
746             case VLC_FOURCC('U','Y','V','Y'):
747             case VLC_FOURCC('U','Y','N','V'):
748             case VLC_FOURCC('Y','4','2','2'):
749                 p_vout->output.i_chroma = VLC_FOURCC('U','Y','V','Y');
750                 i_sdl_chroma = SDL_UYVY_OVERLAY;
751                 break;
752             case VLC_FOURCC('Y','V','Y','U'):
753                 p_vout->output.i_chroma = VLC_FOURCC('Y','V','Y','U');
754                 i_sdl_chroma = SDL_YVYU_OVERLAY;
755                 break;
756             case VLC_FOURCC('Y','V','1','2'):
757             case VLC_FOURCC('I','4','2','0'):
758             case VLC_FOURCC('I','Y','U','V'):
759             default:
760                 p_vout->output.i_chroma = VLC_FOURCC('Y','V','1','2');
761                 i_sdl_chroma = SDL_YV12_OVERLAY;
762                 break;
763         }
764         free( psz_chroma ); psz_chroma = NULL;
765
766         p_vout->p_sys->p_overlay =
767             SDL_CreateYUVOverlay( 32, 32, i_sdl_chroma,
768                                   p_vout->p_sys->p_display );
769         /* FIXME: if the first overlay we find is software, don't stop,
770          * because we may find a hardware one later ... */
771     }
772     while( i_chroma && !p_vout->p_sys->p_overlay );
773
774
775     /* If this best choice failed, fall back to other chromas */
776     if( p_vout->p_sys->p_overlay == NULL )
777     {
778         p_vout->output.i_chroma = VLC_FOURCC('I','Y','U','V');
779         p_vout->p_sys->p_overlay =
780             SDL_CreateYUVOverlay( 32, 32, SDL_IYUV_OVERLAY,
781                                   p_vout->p_sys->p_display );
782     }
783
784     if( p_vout->p_sys->p_overlay == NULL )
785     {
786         p_vout->output.i_chroma = VLC_FOURCC('Y','V','1','2');
787         p_vout->p_sys->p_overlay =
788             SDL_CreateYUVOverlay( 32, 32, SDL_YV12_OVERLAY,
789                                   p_vout->p_sys->p_display );
790     }
791
792     if( p_vout->p_sys->p_overlay == NULL )
793     {
794         p_vout->output.i_chroma = VLC_FOURCC('Y','U','Y','2');
795         p_vout->p_sys->p_overlay =
796             SDL_CreateYUVOverlay( 32, 32, SDL_YUY2_OVERLAY,
797                                   p_vout->p_sys->p_display );
798     }
799
800     if( p_vout->p_sys->p_overlay == NULL )
801     {
802         msg_Warn( p_vout, "no SDL overlay for 0x%.8x (%4.4s)",
803                   p_vout->render.i_chroma, (char*)&p_vout->render.i_chroma );
804
805         switch( p_vout->p_sys->p_display->format->BitsPerPixel )
806         {
807             case 8:
808                 p_vout->output.i_chroma = VLC_FOURCC('R','G','B','2');
809                 p_vout->output.pf_setpalette = SetPalette;
810                 break;
811             case 15:
812                 p_vout->output.i_chroma = VLC_FOURCC('R','V','1','5');
813                 break;
814             case 16:
815                 p_vout->output.i_chroma = VLC_FOURCC('R','V','1','6');
816                 break;
817             case 24:
818                 p_vout->output.i_chroma = VLC_FOURCC('R','V','2','4');
819                 break;
820             case 32:
821                 p_vout->output.i_chroma = VLC_FOURCC('R','V','3','2');
822                 break;
823             default:
824                 msg_Err( p_vout, "unknown screen depth %i",
825                          p_vout->p_sys->p_display->format->BitsPerPixel );
826                 SDL_UnlockSurface( p_vout->p_sys->p_display );
827                 SDL_FreeSurface( p_vout->p_sys->p_display );
828                 return VLC_EGENERIC;
829         }
830
831         p_vout->output.i_rmask = p_vout->p_sys->p_display->format->Rmask;
832         p_vout->output.i_gmask = p_vout->p_sys->p_display->format->Gmask;
833         p_vout->output.i_bmask = p_vout->p_sys->p_display->format->Bmask;
834
835         SDL_WM_SetCaption( VOUT_TITLE " (software RGB SDL output)",
836                            VOUT_TITLE " (software RGB SDL output)" );
837     }
838     else
839     {
840         if( p_vout->p_sys->p_overlay->hw_overlay )
841         {
842             SDL_WM_SetCaption( VOUT_TITLE " (hardware YUV SDL output)",
843                                VOUT_TITLE " (hardware YUV SDL output)" );
844         }
845         else
846         {
847             SDL_WM_SetCaption( VOUT_TITLE " (software YUV SDL output)",
848                                VOUT_TITLE " (software YUV SDL output)" );
849         }
850     }
851
852     SDL_EventState( SDL_KEYUP, SDL_IGNORE );               /* ignore keys up */
853
854     return VLC_SUCCESS;
855 }
856
857 /*****************************************************************************
858  * CloseDisplay: close and reset SDL device
859  *****************************************************************************
860  * This function returns all resources allocated by OpenDisplay and restore
861  * the original state of the device.
862  *****************************************************************************/
863 static void CloseDisplay( vout_thread_t *p_vout )
864 {
865     SDL_FreeYUVOverlay( p_vout->p_sys->p_overlay );
866     SDL_UnlockSurface ( p_vout->p_sys->p_display );
867     SDL_FreeSurface( p_vout->p_sys->p_display );
868 }
869
870 /*****************************************************************************
871  * NewPicture: allocate a picture
872  *****************************************************************************
873  * Returns 0 on success, -1 otherwise
874  *****************************************************************************/
875 static int NewPicture( vout_thread_t *p_vout, picture_t *p_pic )
876 {
877     int i_width  = p_vout->output.i_width;
878     int i_height = p_vout->output.i_height;
879
880     if( p_vout->p_sys->p_overlay == NULL )
881     {
882         /* RGB picture */
883         if( p_vout->p_sys->i_surfaces )
884         {
885             /* We already allocated this surface, return */
886             return VLC_EGENERIC;
887         }
888
889         p_pic->p_sys = malloc( sizeof( picture_sys_t ) );
890
891         if( p_pic->p_sys == NULL )
892         {
893             return VLC_ENOMEM;
894         }
895
896         switch( p_vout->p_sys->p_display->format->BitsPerPixel )
897         {
898             case 8:
899                 p_pic->p->i_pixel_pitch = 1;
900                 break;
901             case 15:
902             case 16:
903                 p_pic->p->i_pixel_pitch = 2;
904                 break;
905             case 24:
906             case 32:
907                 p_pic->p->i_pixel_pitch = 4;
908                 break;
909             default:
910                 return VLC_EGENERIC;
911         }
912
913         p_pic->p->p_pixels = p_vout->p_sys->p_display->pixels;
914         p_pic->p->i_lines = p_vout->p_sys->p_display->h;
915         p_pic->p->i_visible_lines = p_vout->p_sys->p_display->h;
916         p_pic->p->i_pitch = p_vout->p_sys->p_display->pitch;
917         p_pic->p->i_visible_pitch =
918             p_pic->p->i_pixel_pitch * p_vout->p_sys->p_display->w;
919
920         p_vout->p_sys->i_surfaces++;
921
922         p_pic->i_planes = 1;
923     }
924     else
925     {
926         p_pic->p_sys = malloc( sizeof( picture_sys_t ) );
927
928         if( p_pic->p_sys == NULL )
929         {
930             return VLC_ENOMEM;
931         }
932
933         p_pic->p_sys->p_overlay =
934             SDL_CreateYUVOverlay( i_width, i_height,
935                                   p_vout->output.i_chroma,
936                                   p_vout->p_sys->p_display );
937
938         if( p_pic->p_sys->p_overlay == NULL )
939         {
940             free( p_pic->p_sys );
941             return VLC_EGENERIC;
942         }
943
944         SDL_LockYUVOverlay( p_pic->p_sys->p_overlay );
945
946         p_pic->Y_PIXELS = p_pic->p_sys->p_overlay->pixels[0];
947         p_pic->p[Y_PLANE].i_lines = p_pic->p_sys->p_overlay->h;
948         p_pic->p[Y_PLANE].i_visible_lines = p_pic->p_sys->p_overlay->h;
949         p_pic->p[Y_PLANE].i_pitch = p_pic->p_sys->p_overlay->pitches[0];
950
951         switch( p_vout->output.i_chroma )
952         {
953         case SDL_YV12_OVERLAY:
954             p_pic->p[Y_PLANE].i_pixel_pitch = 1;
955             p_pic->p[Y_PLANE].i_visible_pitch = p_pic->p_sys->p_overlay->w;
956
957             p_pic->U_PIXELS = p_pic->p_sys->p_overlay->pixels[2];
958             p_pic->p[U_PLANE].i_lines = p_pic->p_sys->p_overlay->h / 2;
959             p_pic->p[U_PLANE].i_visible_lines = p_pic->p_sys->p_overlay->h / 2;
960             p_pic->p[U_PLANE].i_pitch = p_pic->p_sys->p_overlay->pitches[2];
961             p_pic->p[U_PLANE].i_pixel_pitch = 1;
962             p_pic->p[U_PLANE].i_visible_pitch = p_pic->p_sys->p_overlay->w / 2;
963
964             p_pic->V_PIXELS = p_pic->p_sys->p_overlay->pixels[1];
965             p_pic->p[V_PLANE].i_lines = p_pic->p_sys->p_overlay->h / 2;
966             p_pic->p[V_PLANE].i_visible_lines = p_pic->p_sys->p_overlay->h / 2;
967             p_pic->p[V_PLANE].i_pitch = p_pic->p_sys->p_overlay->pitches[1];
968             p_pic->p[V_PLANE].i_pixel_pitch = 1;
969             p_pic->p[V_PLANE].i_visible_pitch = p_pic->p_sys->p_overlay->w / 2;
970
971             p_pic->i_planes = 3;
972             break;
973
974         case SDL_IYUV_OVERLAY:
975             p_pic->p[Y_PLANE].i_pixel_pitch = 1;
976             p_pic->p[Y_PLANE].i_visible_pitch = p_pic->p_sys->p_overlay->w;
977
978             p_pic->U_PIXELS = p_pic->p_sys->p_overlay->pixels[1];
979             p_pic->p[U_PLANE].i_lines = p_pic->p_sys->p_overlay->h / 2;
980             p_pic->p[U_PLANE].i_visible_lines = p_pic->p_sys->p_overlay->h / 2;
981             p_pic->p[U_PLANE].i_pitch = p_pic->p_sys->p_overlay->pitches[1];
982             p_pic->p[U_PLANE].i_pixel_pitch = 1;
983             p_pic->p[U_PLANE].i_visible_pitch = p_pic->p_sys->p_overlay->w / 2;
984
985             p_pic->V_PIXELS = p_pic->p_sys->p_overlay->pixels[2];
986             p_pic->p[V_PLANE].i_lines = p_pic->p_sys->p_overlay->h / 2;
987             p_pic->p[V_PLANE].i_visible_lines = p_pic->p_sys->p_overlay->h / 2;
988             p_pic->p[V_PLANE].i_pitch = p_pic->p_sys->p_overlay->pitches[2];
989             p_pic->p[V_PLANE].i_pixel_pitch = 1;
990             p_pic->p[V_PLANE].i_visible_pitch = p_pic->p_sys->p_overlay->w / 2;
991
992             p_pic->i_planes = 3;
993             break;
994
995         default:
996             p_pic->p[Y_PLANE].i_pixel_pitch = 2;
997             p_pic->p[U_PLANE].i_visible_pitch = p_pic->p_sys->p_overlay->w * 2;
998
999             p_pic->i_planes = 1;
1000             break;
1001         }
1002     }
1003
1004     return VLC_SUCCESS;
1005 }
1006
1007 /*****************************************************************************
1008  * SetPalette: sets an 8 bpp palette
1009  *****************************************************************************/
1010 static void SetPalette( vout_thread_t *p_vout,
1011                         uint16_t *red, uint16_t *green, uint16_t *blue )
1012 {
1013     SDL_Color colors[256];
1014     int i;
1015
1016     /* Fill colors with color information */
1017     for( i = 0; i < 256; i++ )
1018     {
1019         colors[ i ].r = red[ i ] >> 8;
1020         colors[ i ].g = green[ i ] >> 8;
1021         colors[ i ].b = blue[ i ] >> 8;
1022     }
1023
1024     /* Set palette */
1025     if( SDL_SetColors( p_vout->p_sys->p_display, colors, 0, 256 ) == 0 )
1026     {
1027         msg_Err( p_vout, "failed to set palette" );
1028     }
1029 }
1030