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