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