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