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