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