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