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