sdl.c 35.1 KB
Newer Older
1
/*****************************************************************************
2
 * sdl.c: SDL video output display method
3
 *****************************************************************************
4
 * Copyright (C) 1998-2001 the VideoLAN team
5
 * $Id$
6
7
8
9
10
11
12
13
14
 *
 * Authors: Samuel Hocevar <sam@zoy.org>
 *          Pierre Baillet <oct@zoy.org>
 *          Arnaud de Bossoreille de Ribou <bozo@via.ecp.fr>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
15
 *
16
17
18
19
20
21
22
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
dionoea's avatar
dionoea committed
23
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
24
25
26
27
28
29
30
31
 *****************************************************************************/

/*****************************************************************************
 * Preamble
 *****************************************************************************/
#include <errno.h>                                                 /* ENOMEM */

#include <vlc/vlc.h>
zorglub's avatar
zorglub committed
32
#include <vlc_interface.h>
33
#include <vlc_playlist.h>
zorglub's avatar
zorglub committed
34
#include <vlc_vout.h>
35
36
#include <vlc_keys.h>
//#include <vlc_aout.h>
37
38
39
40
41
42
43
44

#include <sys/types.h>
#ifndef WIN32
#   include <netinet/in.h>                            /* BSD: struct in_addr */
#endif

#include SDL_INCLUDE_FILE

45
46
/* SDL is not able to crop overlays - so use only 1 direct buffer */
#define SDL_MAX_DIRECTBUFFERS 1
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
#define SDL_DEFAULT_BPP 16

/*****************************************************************************
 * vout_sys_t: video output SDL method descriptor
 *****************************************************************************
 * This structure is part of the video output thread descriptor.
 * It describes the SDL specific properties of an output thread.
 *****************************************************************************/
struct vout_sys_t
{
    SDL_Surface *   p_display;                             /* display device */

    int i_width;
    int i_height;

    /* For YUV output */
    SDL_Overlay * p_overlay;   /* An overlay we keep to grab the XVideo port */

    /* For RGB output */
    int i_surfaces;

    vlc_bool_t  b_cursor;
    vlc_bool_t  b_cursor_autohidden;
    mtime_t     i_lastmoved;
    mtime_t     i_lastpressed;                        /* to track dbl-clicks */
};

/*****************************************************************************
 * picture_sys_t: direct buffer method descriptor
 *****************************************************************************
 * This structure is part of the picture descriptor, it describes the
 * SDL specific properties of a direct buffer.
 *****************************************************************************/
struct picture_sys_t
{
    SDL_Overlay *p_overlay;
};

/*****************************************************************************
 * Local prototypes
 *****************************************************************************/
88
89
static int  Open      ( vlc_object_t * );
static void Close     ( vlc_object_t * );
90
91
92
93
94
95
96
97
static int  Init      ( vout_thread_t * );
static void End       ( vout_thread_t * );
static int  Manage    ( vout_thread_t * );
static void Display   ( vout_thread_t *, picture_t * );

static int  OpenDisplay     ( vout_thread_t * );
static void CloseDisplay    ( vout_thread_t * );
static int  NewPicture      ( vout_thread_t *, picture_t * );
98
99
static void SetPalette      ( vout_thread_t *,
                              uint16_t *, uint16_t *, uint16_t * );
100

101
102
103
static int ConvertKey( SDLKey );


104
105
106
107
108
#define CHROMA_TEXT N_("SDL chroma format")
#define CHROMA_LONGTEXT N_( \
    "Force the SDL renderer to use a specific chroma format instead of " \
    "trying to improve performances by using the most efficient one.")

109
110
111
112
/*****************************************************************************
 * Module descriptor
 *****************************************************************************/
vlc_module_begin();
113
114
115
    set_shortname( "SDL" );
    set_category( CAT_VIDEO );
    set_subcategory( SUBCAT_VIDEO_VOUT );
gbazin's avatar
   
gbazin committed
116
    set_description( _("Simple DirectMedia Layer video output") );
117
    set_capability( "video output", 60 );
118
    add_shortcut( "sdl" );
119
    add_string( "sdl-chroma", NULL, NULL, CHROMA_TEXT, CHROMA_LONGTEXT, VLC_TRUE );
120
    set_callbacks( Open, Close );
Christophe Massiot's avatar
Christophe Massiot committed
121
#if defined( __i386__ ) || defined( __x86_64__ )
122
123
124
    /* On i386, SDL is linked against svgalib */
    linked_with_a_crap_library_which_uses_atexit();
#endif
125
126
vlc_module_end();

127
128
129
130
131
132
133
/*****************************************************************************
 * OpenVideo: allocate SDL video thread output method
 *****************************************************************************
 * This function allocate and initialize a SDL vout method. It uses some of the
 * vout properties to choose the correct mode, and change them according to the
 * mode actually used.
 *****************************************************************************/
134
static int Open ( vlc_object_t *p_this )
135
{
136
    vout_thread_t * p_vout = (vout_thread_t *)p_this;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
137
    /* XXX: check for conflicts with the SDL audio output */
138
    vlc_mutex_t *lock = var_AcquireMutex( "sdl" );
139
140
141
142
143

#ifdef HAVE_SETENV
    char *psz_method;
#endif

144
145
146
    if( lock == NULL )
        return VLC_ENOMEM;

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
147
148
149
150
151
152
153
    p_vout->p_sys = malloc( sizeof( vout_sys_t ) );
    if( p_vout->p_sys == NULL )
    {
        vlc_mutex_unlock( lock );
        return VLC_ENOMEM;
    }

154
155
    if( SDL_WasInit( SDL_INIT_VIDEO ) != 0 )
    {
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
156
157
        vlc_mutex_unlock( lock );
        free( p_vout->p_sys );
158
        return VLC_EGENERIC;
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
    }

    /* Allocate structure */

    p_vout->pf_init = Init;
    p_vout->pf_end = End;
    p_vout->pf_manage = Manage;
    p_vout->pf_render = NULL;
    p_vout->pf_display = Display;

#ifdef HAVE_SETENV
    psz_method = config_GetPsz( p_vout, "vout" );
    if( psz_method )
    {
        while( *psz_method && *psz_method != ':' )
        {
            psz_method++;
        }

        if( *psz_method )
        {
            setenv( "SDL_VIDEODRIVER", psz_method + 1, 1 );
        }
    }
#endif

    /* Initialize library */
    if( SDL_Init( SDL_INIT_VIDEO
#ifndef WIN32
    /* Win32 SDL implementation doesn't support SDL_INIT_EVENTTHREAD yet*/
                | SDL_INIT_EVENTTHREAD
#endif
#ifdef DEBUG
    /* In debug mode you may want vlc to dump a core instead of staying
     * stuck */
                | SDL_INIT_NOPARACHUTE
#endif
                ) < 0 )
    {
        msg_Err( p_vout, "cannot initialize SDL (%s)", SDL_GetError() );
        free( p_vout->p_sys );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
200
        vlc_mutex_unlock( lock );
201
        return VLC_EGENERIC;
202
203
    }

204
205
206
    /* Translate keys into unicode */
    SDL_EnableUNICODE(1);

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
207
    vlc_mutex_unlock( lock );
208

209
210
211
212
213
214
215
216
217
    p_vout->p_sys->b_cursor = 1;
    p_vout->p_sys->b_cursor_autohidden = 0;
    p_vout->p_sys->i_lastmoved = mdate();

    if( OpenDisplay( p_vout ) )
    {
        msg_Err( p_vout, "cannot set up SDL (%s)", SDL_GetError() );
        SDL_QuitSubSystem( SDL_INIT_VIDEO );
        free( p_vout->p_sys );
218
        return VLC_EGENERIC;
219
220
    }

221
    return VLC_SUCCESS;
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
}

/*****************************************************************************
 * Init: initialize SDL video thread output method
 *****************************************************************************
 * This function initialize the SDL display device.
 *****************************************************************************/
static int Init( vout_thread_t *p_vout )
{
    int i_index;
    picture_t *p_pic;

    p_vout->p_sys->i_surfaces = 0;

    I_OUTPUTPICTURES = 0;

    /* Initialize the output structure */
    if( p_vout->p_sys->p_overlay == NULL )
    {
        /* All we have is an RGB image with square pixels */
        p_vout->output.i_width  = p_vout->p_sys->i_width;
        p_vout->output.i_height = p_vout->p_sys->i_height;
        p_vout->output.i_aspect = p_vout->output.i_width
                                   * VOUT_ASPECT_FACTOR
                                   / p_vout->output.i_height;
    }
    else
    {
        /* We may need to convert the chroma, but at least we keep the
         * aspect ratio */
        p_vout->output.i_width  = p_vout->render.i_width;
        p_vout->output.i_height = p_vout->render.i_height;
        p_vout->output.i_aspect = p_vout->render.i_aspect;
    }

    /* Try to initialize SDL_MAX_DIRECTBUFFERS direct buffers */
    while( I_OUTPUTPICTURES < SDL_MAX_DIRECTBUFFERS )
    {
        p_pic = NULL;

        /* Find an empty picture slot */
        for( i_index = 0 ; i_index < VOUT_MAX_PICTURES ; i_index++ )
        {
            if( p_vout->p_picture[ i_index ].i_status == FREE_PICTURE )
            {
                p_pic = p_vout->p_picture + i_index;
                break;
            }
        }

        /* Allocate the picture if we found one */
        if( p_pic == NULL || NewPicture( p_vout, p_pic ) )
        {
            break;
        }

        p_pic->i_status = DESTROYED_PICTURE;
        p_pic->i_type   = DIRECT_PICTURE;

        PP_OUTPUTPICTURE[ I_OUTPUTPICTURES ] = p_pic;

        I_OUTPUTPICTURES++;
    }

286
    return VLC_SUCCESS;
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
}

/*****************************************************************************
 * End: terminate Sys video thread output method
 *****************************************************************************
 * Terminate an output method created by OpenVideo
 *****************************************************************************/
static void End( vout_thread_t *p_vout )
{
    int i_index;

    /* Free the output buffers we allocated */
    for( i_index = I_OUTPUTPICTURES ; i_index ; )
    {
        i_index--;
        if( p_vout->p_sys->p_overlay == NULL )
        {
            /* RGB picture */
        }
        else
        {
            SDL_UnlockYUVOverlay(
                    PP_OUTPUTPICTURE[ i_index ]->p_sys->p_overlay );
            SDL_FreeYUVOverlay(
                    PP_OUTPUTPICTURE[ i_index ]->p_sys->p_overlay );
        }
        free( PP_OUTPUTPICTURE[ i_index ]->p_sys );
    }
}

/*****************************************************************************
 * CloseVideo: destroy Sys video thread output method
 *****************************************************************************
 * Terminate an output method created by vout_SDLCreate
 *****************************************************************************/
322
static void Close ( vlc_object_t *p_this )
323
{
324
    vout_thread_t * p_vout = (vout_thread_t *)p_this;
325
326
327
328
329
330
331
332
333
334
335

    CloseDisplay( p_vout );
    SDL_QuitSubSystem( SDL_INIT_VIDEO );

    free( p_vout->p_sys );
}

/*****************************************************************************
 * Manage: handle Sys events
 *****************************************************************************
 * This function should be called regularly by video output thread. It returns
Sam Hocevar's avatar
Sam Hocevar committed
336
 * a non null value if an error occurred.
337
338
339
340
 *****************************************************************************/
static int Manage( vout_thread_t *p_vout )
{
    SDL_Event event;                                            /* SDL event */
341
    vlc_value_t val;
342
    unsigned int i_width, i_height, i_x, i_y;
343
344

    /* Process events */
345
    while( SDL_PollEvent( &event ) )
346
347
348
349
350
351
352
353
354
355
    {
        switch( event.type )
        {
        case SDL_VIDEORESIZE:                          /* Resizing of window */
            /* Update dimensions */
            p_vout->i_changes |= VOUT_SIZE_CHANGE;
            p_vout->i_window_width = p_vout->p_sys->i_width = event.resize.w;
            p_vout->i_window_height = p_vout->p_sys->i_height = event.resize.h;
            break;

356
        /* Mouse move */
357
        case SDL_MOUSEMOTION:
358
359
360
361
362
363
364
365
366
367
368
369
370
371
            vout_PlacePicture( p_vout, p_vout->p_sys->i_width,
                               p_vout->p_sys->i_height,
                               &i_x, &i_y, &i_width, &i_height );

            val.i_int = ( event.motion.x - i_x )
                         * p_vout->render.i_width / i_width;
            var_Set( p_vout, "mouse-x", val );
            val.i_int = ( event.motion.y - i_y )
                         * p_vout->render.i_height / i_height;
            var_Set( p_vout, "mouse-y", val );

            val.b_bool = VLC_TRUE;
            var_Set( p_vout, "mouse-moved", val );

372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
            if( p_vout->p_sys->b_cursor &&
                (abs(event.motion.xrel) > 2 || abs(event.motion.yrel) > 2) )
            {
                if( p_vout->p_sys->b_cursor_autohidden )
                {
                    p_vout->p_sys->b_cursor_autohidden = 0;
                    SDL_ShowCursor( 1 );
                }
                else
                {
                    p_vout->p_sys->i_lastmoved = mdate();
                }
            }
            break;

ivoire's avatar
ivoire committed
387
        /* Mouse button released */
388
389
390
        case SDL_MOUSEBUTTONUP:
            switch( event.button.button )
            {
391
            case SDL_BUTTON_LEFT:
392
393
394
395
                var_Get( p_vout, "mouse-button-down", &val );
                val.i_int &= ~1;
                var_Set( p_vout, "mouse-button-down", val );

396
397
398
399
                val.b_bool = VLC_TRUE;
                var_Set( p_vout, "mouse-clicked", val );
                break;

400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
            case SDL_BUTTON_MIDDLE:
                {
                    playlist_t *p_playlist;

                    var_Get( p_vout, "mouse-button-down", &val );
                    val.i_int &= ~2;
                    var_Set( p_vout, "mouse-button-down", val );

                    p_playlist = vlc_object_find( p_vout, VLC_OBJECT_PLAYLIST,
                                              FIND_ANYWHERE );
                    if( p_playlist != NULL )
                    {
                        vlc_value_t val;
                        var_Get( p_playlist, "intf-show", &val );
                        val.b_bool = !val.b_bool;
                        var_Set( p_playlist, "intf-show", val );
                        vlc_object_release( p_playlist );
                    }
                }
                break;

421
422
423
            case SDL_BUTTON_RIGHT:
                {
                    intf_thread_t *p_intf;
424
425
426
427
428
                    playlist_t *p_playlist;

                    var_Get( p_vout, "mouse-button-down", &val );
                    val.i_int &= ~4;
                    var_Set( p_vout, "mous-button-down", val );
429
430
431
432
433
434
435
                    p_intf = vlc_object_find( p_vout, VLC_OBJECT_INTF,
                                                      FIND_ANYWHERE );
                    if( p_intf )
                    {
                        p_intf->b_menu_change = 1;
                        vlc_object_release( p_intf );
                    }
436
437
438
439
440
441
442
443
444
445
446

                    p_playlist = vlc_object_find( p_vout, VLC_OBJECT_PLAYLIST,
                                                FIND_ANYWHERE );

                    if( p_playlist != NULL )
                    {
                        vlc_value_t val;
                        val.b_bool = VLC_TRUE;
                        var_Set( p_playlist, "intf-popupmenu", val );
                        vlc_object_release( p_playlist );
                    }
447
448
449
450
451
                }
                break;
            }
            break;

ivoire's avatar
ivoire committed
452
        /* Mouse button pressed */
453
454
455
456
        case SDL_MOUSEBUTTONDOWN:
            switch( event.button.button )
            {
            case SDL_BUTTON_LEFT:
457
458
459
                var_Get( p_vout, "mouse-button-down", &val );
                val.i_int |= 1;
                var_Set( p_vout, "mouse-button-down", val );
460
461
462
463
464
465
466
467

                /* detect double-clicks */
                if( ( mdate() - p_vout->p_sys->i_lastpressed ) < 300000 )
                    p_vout->i_changes |= VOUT_FULLSCREEN_CHANGE;

                p_vout->p_sys->i_lastpressed = mdate();
                break;

468
469
470
471
            case SDL_BUTTON_MIDDLE:
                var_Get( p_vout, "mouse-button-down", &val );
                val.i_int |= 2;
                var_Set( p_vout, "mouse-button-down", val );
472
473
                break;

474
475
476
477
            case SDL_BUTTON_RIGHT:
                var_Get( p_vout, "mouse-button-down", &val );
                val.i_int |= 4;
                var_Set( p_vout, "mouse-button-down", val );
478
479
480
481
                break;
            }
            break;

482
        /* Quit event (close the window) */
483
        case SDL_QUIT:
484
485
486
487
488
489
490
491
            {
                playlist_t *p_playlist = (playlist_t *)vlc_object_find( p_vout, VLC_OBJECT_PLAYLIST, FIND_ANYWHERE );
                if( p_playlist != NULL )
                {
                    playlist_Stop( p_playlist );
                    vlc_object_release( p_playlist );
                }
            }
492
493
            break;

494
495
496
        /* Key pressed */
        case SDL_KEYDOWN:
            val.i_int = ConvertKey( event.key.keysym.sym );
497

498
            if( !val.i_int )
499
            {
500
                if( ( event.key.keysym.unicode & 0xff80 ) == 0 )
501
                {
502
                    val.i_int = event.key.keysym.unicode & 0x7f;
503
                }
504
            }
505

506
507
508
            if( val.i_int )
            {
                if( ( event.key.keysym.mod & KMOD_SHIFT ) )
509
                {
510
                    val.i_int |= KEY_MODIFIER_SHIFT;
511
                }
512
                if( ( event.key.keysym.mod & KMOD_CTRL ) )
513
                {
514
                    val.i_int |= KEY_MODIFIER_CTRL;
515
                }
516
                if( ( event.key.keysym.mod & KMOD_ALT ) )
517
                {
518
                    val.i_int |= KEY_MODIFIER_ALT;
519
                }
520
                var_Set( p_vout->p_libvlc, "key-pressed", val );
521
522
523
524
525
526
527
528
529
530
            }

        default:
            break;
        }
    }

    /* Fullscreen change */
    if( p_vout->i_changes & VOUT_FULLSCREEN_CHANGE )
    {
531
532
533
534
535
536
537
        vlc_value_t val_fs;

        /* Update the object variable and trigger callback */
        val_fs.b_bool = !p_vout->b_fullscreen;
        p_vout->b_fullscreen = !p_vout->b_fullscreen;
        var_Set( p_vout, "fullscreen", val_fs );

ivoire's avatar
ivoire committed
538
        /*TODO: add the "always on top" code here !*/
539
540
541
542
543
544
545
546
547

        p_vout->p_sys->b_cursor_autohidden = 0;
        SDL_ShowCursor( p_vout->p_sys->b_cursor &&
                        ! p_vout->p_sys->b_cursor_autohidden );

        p_vout->i_changes &= ~VOUT_FULLSCREEN_CHANGE;
        p_vout->i_changes |= VOUT_SIZE_CHANGE;
    }

ivoire's avatar
ivoire committed
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
    /* Crop or Aspect Ratio Changes */
    if( p_vout->i_changes & VOUT_CROP_CHANGE ||
        p_vout->i_changes & VOUT_ASPECT_CHANGE )
    {
        p_vout->i_changes &= ~VOUT_CROP_CHANGE;
        p_vout->i_changes &= ~VOUT_ASPECT_CHANGE;

        p_vout->fmt_out.i_x_offset = p_vout->fmt_in.i_x_offset;
        p_vout->fmt_out.i_y_offset = p_vout->fmt_in.i_y_offset;
        p_vout->fmt_out.i_visible_width = p_vout->fmt_in.i_visible_width;
        p_vout->fmt_out.i_visible_height = p_vout->fmt_in.i_visible_height;
        p_vout->fmt_out.i_aspect = p_vout->fmt_in.i_aspect;
        p_vout->fmt_out.i_sar_num = p_vout->fmt_in.i_sar_num;
        p_vout->fmt_out.i_sar_den = p_vout->fmt_in.i_sar_den;
        p_vout->output.i_aspect = p_vout->fmt_in.i_aspect;

        p_vout->i_changes |= VOUT_SIZE_CHANGE;
    }

567
    /* Size change */
568
569
570
571
    if( p_vout->i_changes & VOUT_SIZE_CHANGE )
    {
        msg_Dbg( p_vout, "video display resized (%dx%d)",
                 p_vout->p_sys->i_width, p_vout->p_sys->i_height );
572

573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
        CloseDisplay( p_vout );
        OpenDisplay( p_vout );

        /* We don't need to signal the vout thread about the size change if
         * we can handle rescaling ourselves */
        if( p_vout->p_sys->p_overlay != NULL )
            p_vout->i_changes &= ~VOUT_SIZE_CHANGE;
    }

    /* Pointer change */
    if( ! p_vout->p_sys->b_cursor_autohidden &&
        ( mdate() - p_vout->p_sys->i_lastmoved > 2000000 ) )
    {
        /* Hide the mouse automatically */
        p_vout->p_sys->b_cursor_autohidden = 1;
        SDL_ShowCursor( 0 );
    }

591
    return VLC_SUCCESS;
592
593
}

594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
/*****************************************************************************
 * Key events handling
 *****************************************************************************/
static struct
{
    SDLKey sdl_key;
    int i_vlckey;
} sdlkeys_to_vlckeys[] =
{
    { SDLK_F1,  KEY_F1 },
    { SDLK_F2,  KEY_F2 },
    { SDLK_F3,  KEY_F3 },
    { SDLK_F4,  KEY_F4 },
    { SDLK_F5,  KEY_F5 },
    { SDLK_F6,  KEY_F6 },
    { SDLK_F7,  KEY_F7 },
    { SDLK_F8,  KEY_F8 },
    { SDLK_F9,  KEY_F9 },
    { SDLK_F10, KEY_F10 },
    { SDLK_F11, KEY_F11 },
    { SDLK_F12, KEY_F12 },

    { SDLK_RETURN, KEY_ENTER },
    { SDLK_KP_ENTER, KEY_ENTER },
    { SDLK_SPACE, KEY_SPACE },
    { SDLK_ESCAPE, KEY_ESC },

    { SDLK_MENU, KEY_MENU },
    { SDLK_LEFT, KEY_LEFT },
    { SDLK_RIGHT, KEY_RIGHT },
    { SDLK_UP, KEY_UP },
    { SDLK_DOWN, KEY_DOWN },

    { SDLK_HOME, KEY_HOME },
    { SDLK_END, KEY_END },
    { SDLK_PAGEUP, KEY_PAGEUP },
    { SDLK_PAGEDOWN,  KEY_PAGEDOWN },

    { SDLK_INSERT, KEY_INSERT },
    { SDLK_DELETE, KEY_DELETE },
    /*TODO: find a equivalent for SDL 
    { , KEY_MEDIA_NEXT_TRACK }
    { , KEY_MEDIA_PREV_TRACK }
    { , KEY_VOLUME_MUTE }
    { , KEY_VOLUME_DOWN }
    { , KEY_VOLUME_UP }
    { , KEY_MEDIA_PLAY_PAUSE }
    { , KEY_MEDIA_PLAY_PAUSE }*/

    { 0, 0 }
};

static int ConvertKey( SDLKey sdl_key )
{
    int i;
    for( i=0; sdlkeys_to_vlckeys[i].sdl_key != 0; i++ )
    {
        if( sdlkeys_to_vlckeys[i].sdl_key == sdl_key )
        {
            return sdlkeys_to_vlckeys[i].i_vlckey;
        }
    }
    return 0;
}


660
661
662
663
664
665
666
/*****************************************************************************
 * Display: displays previously rendered output
 *****************************************************************************
 * This function sends the currently rendered image to the display.
 *****************************************************************************/
static void Display( vout_thread_t *p_vout, picture_t *p_pic )
{
667
    unsigned int x, y, w, h;
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
    SDL_Rect disp;

    vout_PlacePicture( p_vout, p_vout->p_sys->i_width, p_vout->p_sys->i_height,
                       &x, &y, &w, &h );
    disp.x = x;
    disp.y = y;
    disp.w = w;
    disp.h = h;

    if( p_vout->p_sys->p_overlay == NULL )
    {
        /* RGB picture */
        SDL_Flip( p_vout->p_sys->p_display );
    }
    else
    {
        /* Overlay picture */
        SDL_UnlockYUVOverlay( p_pic->p_sys->p_overlay);
        SDL_DisplayYUVOverlay( p_pic->p_sys->p_overlay , &disp );
        SDL_LockYUVOverlay( p_pic->p_sys->p_overlay);
    }
}

/* following functions are local */

/*****************************************************************************
 * OpenDisplay: open and initialize SDL device
 *****************************************************************************
 * Open and initialize display according to preferences specified in the vout
 * thread fields.
 *****************************************************************************/
static int OpenDisplay( vout_thread_t *p_vout )
{
Sam Hocevar's avatar
Sam Hocevar committed
701
702
    uint32_t i_flags;
    int i_bpp;
703
704
705

    /* SDL fucked up fourcc definitions on bigendian machines */
    uint32_t i_sdl_chroma;
706
707
    char *psz_chroma = NULL;
    uint32_t i_chroma = 0;
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723

    /* Set main window's size */
    p_vout->p_sys->i_width = p_vout->b_fullscreen ? p_vout->output.i_width :
                                                    p_vout->i_window_width;
    p_vout->p_sys->i_height = p_vout->b_fullscreen ? p_vout->output.i_height :
                                                     p_vout->i_window_height;

    /* Initialize flags and cursor */
    i_flags = SDL_ANYFORMAT | SDL_HWPALETTE | SDL_HWSURFACE | SDL_DOUBLEBUF;
    i_flags |= p_vout->b_fullscreen ? SDL_FULLSCREEN : SDL_RESIZABLE;

    i_bpp = SDL_VideoModeOK( p_vout->p_sys->i_width, p_vout->p_sys->i_height,
                             SDL_DEFAULT_BPP, i_flags );
    if( i_bpp == 0 )
    {
        msg_Err( p_vout, "no video mode available" );
724
        return VLC_EGENERIC;
725
726
727
728
729
730
731
732
733
    }

    p_vout->p_sys->p_display = SDL_SetVideoMode( p_vout->p_sys->i_width,
                                                 p_vout->p_sys->i_height,
                                                 i_bpp, i_flags );

    if( p_vout->p_sys->p_display == NULL )
    {
        msg_Err( p_vout, "cannot set video mode" );
734
        return VLC_EGENERIC;
735
736
737
738
    }

    SDL_LockSurface( p_vout->p_sys->p_display );

739
740
741
742
743
744
745
746
747
748
749
750
751
752
    if( ( psz_chroma = config_GetPsz( p_vout, "sdl-chroma" ) ) )
    {
        if( strlen( psz_chroma ) >= 4 )
        {
            memcpy(&i_chroma, psz_chroma, 4);
            msg_Dbg( p_vout, "Forcing chroma to 0x%.8x (%4.4s)", i_chroma, (char*)&i_chroma );
        }
        else
        {
            free( psz_chroma );
            psz_chroma = NULL;
        }
    }

753
    /* Choose the chroma we will try first. */
754
    do
755
    {
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
        if( !psz_chroma ) i_chroma = 0;
        switch( i_chroma ? i_chroma : p_vout->render.i_chroma )
        {
            case VLC_FOURCC('Y','U','Y','2'):
            case VLC_FOURCC('Y','U','N','V'):
                p_vout->output.i_chroma = VLC_FOURCC('Y','U','Y','2');
                i_sdl_chroma = SDL_YUY2_OVERLAY;
                break;
            case VLC_FOURCC('U','Y','V','Y'):
            case VLC_FOURCC('U','Y','N','V'):
            case VLC_FOURCC('Y','4','2','2'):
                p_vout->output.i_chroma = VLC_FOURCC('U','Y','V','Y');
                i_sdl_chroma = SDL_UYVY_OVERLAY;
                break;
            case VLC_FOURCC('Y','V','Y','U'):
                p_vout->output.i_chroma = VLC_FOURCC('Y','V','Y','U');
                i_sdl_chroma = SDL_YVYU_OVERLAY;
                break;
            case VLC_FOURCC('Y','V','1','2'):
            case VLC_FOURCC('I','4','2','0'):
            case VLC_FOURCC('I','Y','U','V'):
            default:
                p_vout->output.i_chroma = VLC_FOURCC('Y','V','1','2');
                i_sdl_chroma = SDL_YV12_OVERLAY;
                break;
        }
        free( psz_chroma ); psz_chroma = NULL;

        p_vout->p_sys->p_overlay =
            SDL_CreateYUVOverlay( 32, 32, i_sdl_chroma,
                                  p_vout->p_sys->p_display );
        /* FIXME: if the first overlay we find is software, don't stop,
         * because we may find a hardware one later ... */
789
    }
790
    while( i_chroma && !p_vout->p_sys->p_overlay );
791
792
793
794
795


    /* If this best choice failed, fall back to other chromas */
    if( p_vout->p_sys->p_overlay == NULL )
    {
796
        p_vout->output.i_chroma = VLC_FOURCC('I','Y','U','V');
797
        p_vout->p_sys->p_overlay =
798
            SDL_CreateYUVOverlay( 32, 32, SDL_IYUV_OVERLAY,
799
800
801
802
803
                                  p_vout->p_sys->p_display );
    }

    if( p_vout->p_sys->p_overlay == NULL )
    {
804
        p_vout->output.i_chroma = VLC_FOURCC('Y','V','1','2');
805
        p_vout->p_sys->p_overlay =
806
            SDL_CreateYUVOverlay( 32, 32, SDL_YV12_OVERLAY,
807
808
809
810
811
                                  p_vout->p_sys->p_display );
    }

    if( p_vout->p_sys->p_overlay == NULL )
    {
812
        p_vout->output.i_chroma = VLC_FOURCC('Y','U','Y','2');
813
        p_vout->p_sys->p_overlay =
814
            SDL_CreateYUVOverlay( 32, 32, SDL_YUY2_OVERLAY,
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
                                  p_vout->p_sys->p_display );
    }

    if( p_vout->p_sys->p_overlay == NULL )
    {
        msg_Warn( p_vout, "no SDL overlay for 0x%.8x (%4.4s)",
                  p_vout->render.i_chroma, (char*)&p_vout->render.i_chroma );

        switch( p_vout->p_sys->p_display->format->BitsPerPixel )
        {
            case 8:
                p_vout->output.i_chroma = VLC_FOURCC('R','G','B','2');
                p_vout->output.pf_setpalette = SetPalette;
                break;
            case 15:
                p_vout->output.i_chroma = VLC_FOURCC('R','V','1','5');
                break;
            case 16:
                p_vout->output.i_chroma = VLC_FOURCC('R','V','1','6');
                break;
            case 24:
                p_vout->output.i_chroma = VLC_FOURCC('R','V','2','4');
                break;
            case 32:
                p_vout->output.i_chroma = VLC_FOURCC('R','V','3','2');
                break;
            default:
                msg_Err( p_vout, "unknown screen depth %i",
                         p_vout->p_sys->p_display->format->BitsPerPixel );
                SDL_UnlockSurface( p_vout->p_sys->p_display );
                SDL_FreeSurface( p_vout->p_sys->p_display );
846
                return VLC_EGENERIC;
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
        }

        p_vout->output.i_rmask = p_vout->p_sys->p_display->format->Rmask;
        p_vout->output.i_gmask = p_vout->p_sys->p_display->format->Gmask;
        p_vout->output.i_bmask = p_vout->p_sys->p_display->format->Bmask;

        SDL_WM_SetCaption( VOUT_TITLE " (software RGB SDL output)",
                           VOUT_TITLE " (software RGB SDL output)" );
    }
    else
    {
        if( p_vout->p_sys->p_overlay->hw_overlay )
        {
            SDL_WM_SetCaption( VOUT_TITLE " (hardware YUV SDL output)",
                               VOUT_TITLE " (hardware YUV SDL output)" );
        }
        else
        {
            SDL_WM_SetCaption( VOUT_TITLE " (software YUV SDL output)",
                               VOUT_TITLE " (software YUV SDL output)" );
        }
    }

    SDL_EventState( SDL_KEYUP, SDL_IGNORE );               /* ignore keys up */

872
    return VLC_SUCCESS;
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
}

/*****************************************************************************
 * CloseDisplay: close and reset SDL device
 *****************************************************************************
 * This function returns all resources allocated by OpenDisplay and restore
 * the original state of the device.
 *****************************************************************************/
static void CloseDisplay( vout_thread_t *p_vout )
{
    SDL_FreeYUVOverlay( p_vout->p_sys->p_overlay );
    SDL_UnlockSurface ( p_vout->p_sys->p_display );
    SDL_FreeSurface( p_vout->p_sys->p_display );
}

/*****************************************************************************
 * NewPicture: allocate a picture
 *****************************************************************************
 * Returns 0 on success, -1 otherwise
 *****************************************************************************/
static int NewPicture( vout_thread_t *p_vout, picture_t *p_pic )
{
    int i_width  = p_vout->output.i_width;
    int i_height = p_vout->output.i_height;

    if( p_vout->p_sys->p_overlay == NULL )
    {
        /* RGB picture */
        if( p_vout->p_sys->i_surfaces )
        {
            /* We already allocated this surface, return */
904
            return VLC_EGENERIC;
905
906
907
908
909
910
        }

        p_pic->p_sys = malloc( sizeof( picture_sys_t ) );

        if( p_pic->p_sys == NULL )
        {
911
            return VLC_ENOMEM;
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
        }

        switch( p_vout->p_sys->p_display->format->BitsPerPixel )
        {
            case 8:
                p_pic->p->i_pixel_pitch = 1;
                break;
            case 15:
            case 16:
                p_pic->p->i_pixel_pitch = 2;
                break;
            case 24:
            case 32:
                p_pic->p->i_pixel_pitch = 4;
                break;
            default:
928
                return VLC_EGENERIC;
929
930
931
932
        }

        p_pic->p->p_pixels = p_vout->p_sys->p_display->pixels;
        p_pic->p->i_lines = p_vout->p_sys->p_display->h;
933
        p_pic->p->i_visible_lines = p_vout->p_sys->p_display->h;
934
935
936
937
938
939
940
941
942
943
944
945
946
947
        p_pic->p->i_pitch = p_vout->p_sys->p_display->pitch;
        p_pic->p->i_visible_pitch =
            p_pic->p->i_pixel_pitch * p_vout->p_sys->p_display->w;

        p_vout->p_sys->i_surfaces++;

        p_pic->i_planes = 1;
    }
    else
    {
        p_pic->p_sys = malloc( sizeof( picture_sys_t ) );

        if( p_pic->p_sys == NULL )
        {
948
            return VLC_ENOMEM;
949
950
951
952
953
954
955
956
957
958
        }

        p_pic->p_sys->p_overlay =
            SDL_CreateYUVOverlay( i_width, i_height,
                                  p_vout->output.i_chroma,
                                  p_vout->p_sys->p_display );

        if( p_pic->p_sys->p_overlay == NULL )
        {
            free( p_pic->p_sys );
959
            return VLC_EGENERIC;
960
961
962
963
964
965
        }

        SDL_LockYUVOverlay( p_pic->p_sys->p_overlay );

        p_pic->Y_PIXELS = p_pic->p_sys->p_overlay->pixels[0];
        p_pic->p[Y_PLANE].i_lines = p_pic->p_sys->p_overlay->h;
966
        p_pic->p[Y_PLANE].i_visible_lines = p_pic->p_sys->p_overlay->h;
967
968
969
970
971
972
        p_pic->p[Y_PLANE].i_pitch = p_pic->p_sys->p_overlay->pitches[0];

        switch( p_vout->output.i_chroma )
        {
        case SDL_YV12_OVERLAY:
            p_pic->p[Y_PLANE].i_pixel_pitch = 1;
973
            p_pic->p[Y_PLANE].i_visible_pitch = p_pic->p_sys->p_overlay->w;
974
975
976

            p_pic->U_PIXELS = p_pic->p_sys->p_overlay->pixels[2];
            p_pic->p[U_PLANE].i_lines = p_pic->p_sys->p_overlay->h / 2;
977
            p_pic->p[U_PLANE].i_visible_lines = p_pic->p_sys->p_overlay->h / 2;
978
979
            p_pic->p[U_PLANE].i_pitch = p_pic->p_sys->p_overlay->pitches[2];
            p_pic->p[U_PLANE].i_pixel_pitch = 1;
980
            p_pic->p[U_PLANE].i_visible_pitch = p_pic->p_sys->p_overlay->w / 2;
981
982
983

            p_pic->V_PIXELS = p_pic->p_sys->p_overlay->pixels[1];
            p_pic->p[V_PLANE].i_lines = p_pic->p_sys->p_overlay->h / 2;
984
            p_pic->p[V_PLANE].i_visible_lines = p_pic->p_sys->p_overlay->h / 2;
985
986
            p_pic->p[V_PLANE].i_pitch = p_pic->p_sys->p_overlay->pitches[1];
            p_pic->p[V_PLANE].i_pixel_pitch = 1;
987
            p_pic->p[V_PLANE].i_visible_pitch = p_pic->p_sys->p_overlay->w / 2;
988
989
990
991
992
993

            p_pic->i_planes = 3;
            break;

        case SDL_IYUV_OVERLAY:
            p_pic->p[Y_PLANE].i_pixel_pitch = 1;
994
            p_pic->p[Y_PLANE].i_visible_pitch = p_pic->p_sys->p_overlay->w;
995
996
997

            p_pic->U_PIXELS = p_pic->p_sys->p_overlay->pixels[1];
            p_pic->p[U_PLANE].i_lines = p_pic->p_sys->p_overlay->h / 2;
998
            p_pic->p[U_PLANE].i_visible_lines = p_pic->p_sys->p_overlay->h / 2;
999
1000
            p_pic->p[U_PLANE].i_pitch = p_pic->p_sys->p_overlay->pitches[1];
            p_pic->p[U_PLANE].i_pixel_pitch = 1;
1001
            p_pic->p[U_PLANE].i_visible_pitch = p_pic->p_sys->p_overlay->w / 2;
1002
1003
1004

            p_pic->V_PIXELS = p_pic->p_sys->p_overlay->pixels[2];
            p_pic->p[V_PLANE].i_lines = p_pic->p_sys->p_overlay->h / 2;
1005
            p_pic->p[V_PLANE].i_visible_lines = p_pic->p_sys->p_overlay->h / 2;
1006
1007
            p_pic->p[V_PLANE].i_pitch = p_pic->p_sys->p_overlay->pitches[2];
            p_pic->p[V_PLANE].i_pixel_pitch = 1;
1008
            p_pic->p[V_PLANE].i_visible_pitch = p_pic->p_sys->p_overlay->w / 2;
1009
1010
1011
1012
1013
1014

            p_pic->i_planes = 3;
            break;

        default:
            p_pic->p[Y_PLANE].i_pixel_pitch = 2;
1015
            p_pic->p[U_PLANE].i_visible_pitch = p_pic->p_sys->p_overlay->w * 2;
1016
1017
1018
1019
1020
1021

            p_pic->i_planes = 1;
            break;
        }
    }

1022
    return VLC_SUCCESS;
1023
1024
1025
1026
1027
}

/*****************************************************************************
 * SetPalette: sets an 8 bpp palette
 *****************************************************************************/
1028
1029
static void SetPalette( vout_thread_t *p_vout,
                        uint16_t *red, uint16_t *green, uint16_t *blue )
1030
1031
1032
{
    SDL_Color colors[256];
    int i;
1033

1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
    /* Fill colors with color information */
    for( i = 0; i < 256; i++ )
    {
        colors[ i ].r = red[ i ] >> 8;
        colors[ i ].g = green[ i ] >> 8;
        colors[ i ].b = blue[ i ] >> 8;
    }

    /* Set palette */
    if( SDL_SetColors( p_vout->p_sys->p_display, colors, 0, 256 ) == 0 )
    {
1045
        msg_Err( p_vout, "failed to set palette" );
1046
1047
1048
    }
}