fb.c 23.6 KB
Newer Older
1
2
3
/*****************************************************************************
 * fb.c : framebuffer plugin for vlc
 *****************************************************************************
4
 * Copyright (C) 2000-2009 the VideoLAN team
5
 * $Id$
6
7
 *
 * Authors: Samuel Hocevar <sam@zoy.org>
8
 *          Jean-Paul Saman
9
 *
10
11
12
13
 * 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.
14
 *
15
16
17
18
19
20
21
 * 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
22
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23
24
25
26
27
 *****************************************************************************/

/*****************************************************************************
 * Preamble
 *****************************************************************************/
28
29
30
31
32

#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

33
34
35
36
37
38
39
40
41
42
43
44
45
#include <errno.h>                                                 /* ENOMEM */
#include <signal.h>                                      /* SIGUSR1, SIGUSR2 */
#include <fcntl.h>                                                 /* open() */
#include <unistd.h>                                               /* close() */

#include <termios.h>                                       /* struct termios */
#include <sys/ioctl.h>
#include <sys/mman.h>                                              /* mmap() */

#include <linux/fb.h>
#include <linux/vt.h>                                                /* VT_* */
#include <linux/kd.h>                                                 /* KD* */

46
#include <vlc_common.h>
47
#include <vlc_plugin.h>
48
49
#include <vlc_vout_display.h>
#include <vlc_picture_pool.h>
50
#include <vlc_charset.h>
51
52
53
54
55
56

/*****************************************************************************
 * Module descriptor
 *****************************************************************************/
#define FB_DEV_VAR "fbdev"

zorglub's avatar
zorglub committed
57
#define DEVICE_TEXT N_("Framebuffer device")
58
#define DEVICE_LONGTEXT N_(\
59
    "Framebuffer device to use for rendering (usually /dev/fb0).")
zorglub's avatar
zorglub committed
60

61
#define TTY_TEXT N_("Run fb on current tty.")
62
#define TTY_LONGTEXT N_(\
63
    "Run framebuffer on current TTY device (default enabled). " \
64
    "(disable tty handling with caution)")
65

66
#define FB_MODE_TEXT N_("Framebuffer resolution to use.")
67
#define FB_MODE_LONGTEXT N_(\
68
    "Select the resolution for the framebuffer. Currently it supports " \
69
    "the values 0=QCIF 1=CIF 2=NTSC 3=PAL, 4=auto (default 4=auto)")
70

71
#define HW_ACCEL_TEXT N_("Framebuffer uses hw acceleration.")
72
#define HW_ACCEL_LONGTEXT N_(\
73
74
    "If your framebuffer supports hardware acceleration or does double buffering " \
    "in hardware then you must disable this option. It then does double buffering " \
75
    "in software.")
76

77
#define CHROMA_TEXT N_("Image format (default RGB).")
Rafaël Carré's avatar
Rafaël Carré committed
78
#define CHROMA_LONGTEXT N_("Chroma fourcc used by the framebuffer. Default is RGB since the fb device has no way to report its chroma.")
79

80
81
static int  Open (vlc_object_t *);
static void Close(vlc_object_t *);
Laurent Aimar's avatar
Laurent Aimar committed
82

83
vlc_module_begin ()
84
85
86
87
88
    set_shortname("Framebuffer")
    set_category(CAT_VIDEO)
    set_subcategory(SUBCAT_VIDEO_VOUT)
    add_file(FB_DEV_VAR, "/dev/fb0", NULL, DEVICE_TEXT, DEVICE_LONGTEXT,
              false)
ivoire's avatar
ivoire committed
89
    add_bool("fb-tty", true, NULL, TTY_TEXT, TTY_LONGTEXT, true)
90
    add_string( "fb-chroma", NULL, NULL, CHROMA_TEXT, CHROMA_LONGTEXT, true )
91
92
93
94
95
96
97
98
    add_obsolete_string("fb-aspect-ratio")
    add_integer("fb-mode", 4, NULL, FB_MODE_TEXT, FB_MODE_LONGTEXT,
                 true)
    add_bool("fb-hw-accel", true, NULL, HW_ACCEL_TEXT, HW_ACCEL_LONGTEXT,
              true)
    set_description(N_("GNU/Linux framebuffer video output"))
    set_capability("vout display", 30)
    set_callbacks(Open, Close)
99
vlc_module_end ()
100

Laurent Aimar's avatar
Laurent Aimar committed
101
102
103
/*****************************************************************************
 * Local prototypes
 *****************************************************************************/
104
105
106
107
static picture_pool_t *Pool  (vout_display_t *, unsigned);
static void           Display(vout_display_t *, picture_t *);
static int            Control(vout_display_t *, int, va_list);
static void           Manage (vout_display_t *);
108
109
110
111
112
113
114
115
116
117
118
119
120

/* */
static int  OpenDisplay  (vout_display_t *, bool force_resolution);
static void CloseDisplay (vout_display_t *);
static void SwitchDisplay(int i_signal);
static void TextMode     (int tty);
static void GfxMode      (int tty);

static int  TtyInit(vout_display_t *);
static void TtyExit(vout_display_t *);

/* */
struct vout_display_sys_t {
121
    /* System information */
122
123
    int                 tty;                          /* tty device handle */
    bool                is_tty;
124
125
    struct termios      old_termios;

126
    /* Original configuration information */
127
128
129
130
131
    struct sigaction            sig_usr1;           /* USR1 previous handler */
    struct sigaction            sig_usr2;           /* USR2 previous handler */
    struct vt_mode              vt_mode;                 /* previous VT mode */

    /* Framebuffer information */
132
    int                         fd;                       /* device handle */
133
134
    struct fb_var_screeninfo    old_info;       /* original mode information */
    struct fb_var_screeninfo    var_info;        /* current mode information */
135
    bool                        has_pan;   /* does device supports panning ? */
136
    struct fb_cmap              fb_cmap;                /* original colormap */
137
138
    uint16_t                    *palette;                /* original palette */
    bool                        is_hw_accel;         /* has hardware support */
139
140

    /* Video information */
141
142
    uint32_t width;
    uint32_t height;
143
    uint32_t line_length;
144
    vlc_fourcc_t chroma;
145
    int      bytes_per_pixel;
146
147

    /* Video memory */
148
149
    uint8_t     *video_ptr;                                  /* base adress */
    size_t      video_size;                                    /* page size */
150

151
152
    picture_t       *picture;
    picture_pool_t  *pool;
153
154
};

155

156
static void ClearScreen(vout_display_sys_t *sys)
157
{
158
    switch (sys->chroma) {
159
    /* XXX: add other chromas */
160
    case VLC_CODEC_UYVY: {
161
162
163
164
165
166
167
168
169
170
171
        unsigned int j, size = sys->video_size / 4;
        uint32_t *ptr = (uint32_t*)((uintptr_t)(sys->video_ptr + 3) & ~3);
        for(j=0; j < size; j++)
            ptr[j] = 0x10801080;    /* U = V = 16, Y = 128 */
        break;
    }
    default:    /* RGB */
        memset(sys->video_ptr, 0, sys->video_size);
    }
}

Laurent Aimar's avatar
Laurent Aimar committed
172
/**
173
 * This function allocates and initializes a FB vout method.
Laurent Aimar's avatar
Laurent Aimar committed
174
 */
175
static int Open(vlc_object_t *object)
176
{
177
178
    vout_display_t     *vd = (vout_display_t *)object;
    vout_display_sys_t *sys;
179
180

    /* Allocate instance and initialize some members */
181
182
    vd->sys = sys = calloc(1, sizeof(*sys));
    if (!sys)
183
        return VLC_ENOMEM;
184

185
    /* Does the framebuffer uses hw acceleration? */
186
    sys->is_hw_accel = var_CreateGetBool(vd, "fb-hw-accel");
187

188
    /* Set tty and fb devices */
189
190
191
    sys->tty = 0; /* 0 == /dev/tty0 == current console */
    sys->is_tty = var_CreateGetBool(vd, "fb-tty");
#if !defined(WIN32) &&  defined(HAVE_ISATTY)
192
    /* Check that stdin is a TTY */
193
194
195
    if (sys->is_tty && !isatty(0)) {
        msg_Warn(vd, "fd 0 is not a TTY");
        free(sys);
196
        return VLC_EGENERIC;
197
    }
198
199
    msg_Warn(vd, "disabling tty handling, use with caution because "
                 "there is no way to return to the tty.");
200
#endif
201

202
203
204
    const int mode = var_CreateGetInteger(vd, "fb-mode");
    bool force_resolution = true;
    switch (mode) {
Laurent Aimar's avatar
Laurent Aimar committed
205
    case 0: /* QCIF */
206
207
        sys->width  = 176;
        sys->height = 144;
Laurent Aimar's avatar
Laurent Aimar committed
208
209
        break;
    case 1: /* CIF */
210
211
        sys->width  = 352;
        sys->height = 288;
Laurent Aimar's avatar
Laurent Aimar committed
212
213
        break;
    case 2: /* NTSC */
214
215
        sys->width  = 640;
        sys->height = 480;
Laurent Aimar's avatar
Laurent Aimar committed
216
217
        break;
    case 3: /* PAL */
218
219
        sys->width  = 704;
        sys->height = 576;
Laurent Aimar's avatar
Laurent Aimar committed
220
221
222
        break;
    case 4:
    default:
223
        force_resolution = false;
Laurent Aimar's avatar
Laurent Aimar committed
224
225
        break;
    }
226

227
228
229
    char *chroma = var_CreateGetNonEmptyString(vd, "fb-chroma");
    if (chroma) {
        sys->chroma = vlc_fourcc_GetCodecFromString(VIDEO_ES, chroma);
230
231

        if (sys->chroma)
232
            msg_Dbg(vd, "forcing chroma '%s'", chroma);
233
        else
234
            msg_Warn(vd, "chroma %s invalid, using default", chroma);
235

236
        free(chroma);
237
238
239
240
    }
    else
        sys->chroma = 0;

241
    /* tty handling */
242
243
    if (sys->is_tty && TtyInit(vd)) {
        free(sys);
Laurent Aimar's avatar
Laurent Aimar committed
244
        return VLC_EGENERIC;
245
246
    }

247
248
249
250
251
252
253
254
255
256
257
258
259
    /* */
    sys->video_ptr = MAP_FAILED;
    sys->picture = NULL;
    sys->pool = NULL;

    if (OpenDisplay(vd, force_resolution)) {
        Close(VLC_OBJECT(vd));
        return VLC_EGENERIC;
    }

    /* */
    video_format_t fmt = vd->fmt;

260
    if (sys->chroma) {
261
        fmt.i_chroma = sys->chroma;
262
    }
263
    else {
264
265
        /* Assume RGB */

266
        msg_Dbg(vd, "%d bppd", sys->var_info.bits_per_pixel);
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
        switch (sys->var_info.bits_per_pixel) {
        case 8: /* FIXME: set the palette */
            fmt.i_chroma = VLC_CODEC_RGB8;
            break;
        case 15:
            fmt.i_chroma = VLC_CODEC_RGB15;
            break;
        case 16:
            fmt.i_chroma = VLC_CODEC_RGB16;
            break;
        case 24:
            fmt.i_chroma = VLC_CODEC_RGB24;
            break;
        case 32:
            fmt.i_chroma = VLC_CODEC_RGB32;
            break;
        default:
            msg_Err(vd, "unknown screendepth %i", sys->var_info.bits_per_pixel);
            Close(VLC_OBJECT(vd));
            return VLC_EGENERIC;
        }
        if (sys->var_info.bits_per_pixel != 8) {
            fmt.i_rmask = ((1 << sys->var_info.red.length) - 1)
                                 << sys->var_info.red.offset;
            fmt.i_gmask = ((1 << sys->var_info.green.length) - 1)
                                 << sys->var_info.green.offset;
            fmt.i_bmask = ((1 << sys->var_info.blue.length) - 1)
                                 << sys->var_info.blue.offset;
        }
296
    }
297

298
299
    fmt.i_width  = sys->width;
    fmt.i_height = sys->height;
300

Laurent Aimar's avatar
Laurent Aimar committed
301
    /* */
302
303
304
305
306
307
    vout_display_info_t info = vd->info;
    info.has_hide_mouse = true;

    /* */
    vd->fmt     = fmt;
    vd->info    = info;
308
    vd->pool    = Pool;
309
310
311
312
313
314
315
    vd->prepare = NULL;
    vd->display = Display;
    vd->control = Control;
    vd->manage  = Manage;

    /* */
    vout_display_SendEventFullscreen(vd, true);
316
    vout_display_SendEventDisplaySize(vd, fmt.i_width, fmt.i_height, true);
317
    return VLC_SUCCESS;
318
319
}

Laurent Aimar's avatar
Laurent Aimar committed
320
321
322
/**
 * Terminate an output method created by Open
 */
323
static void Close(vlc_object_t *object)
324
{
325
326
    vout_display_t *vd = (vout_display_t *)object;
    vout_display_sys_t *sys = vd->sys;
327

328
329
330
331
    if (sys->pool)
        picture_pool_Delete(sys->pool);
    if (!sys->is_hw_accel && sys->picture)
        picture_Release(sys->picture);
332

333
    CloseDisplay(vd);
Laurent Aimar's avatar
Laurent Aimar committed
334

335
336
337
338
    if (sys->is_tty)
        TtyExit(vd);

    free(sys);
Laurent Aimar's avatar
Laurent Aimar committed
339
340
}

341
/* */
342
static picture_pool_t *Pool(vout_display_t *vd, unsigned count)
Laurent Aimar's avatar
Laurent Aimar committed
343
{
344
345
346
347
348
349
350
351
352
    vout_display_sys_t *sys = vd->sys;

    if (!sys->pool) {
        if (!sys->picture) {
            picture_resource_t rsc;

            memset(&rsc, 0, sizeof(rsc));
            rsc.p[0].p_pixels = sys->video_ptr;
            rsc.p[0].i_lines  = sys->var_info.yres;
353
            rsc.p[0].i_pitch = sys->line_length;
354
355
356
357
358

            sys->picture = picture_NewFromResource(&vd->fmt, &rsc);
            if (!sys->picture)
                return NULL;
        }
359

360
361
362
        if (sys->is_hw_accel)
            sys->pool = picture_pool_New(1, &sys->picture);
        else
363
            sys->pool = picture_pool_NewFromFormat(&vd->fmt, count);
Laurent Aimar's avatar
Laurent Aimar committed
364
    }
365
    return sys->pool;
366
367
368
369
}
static void Display(vout_display_t *vd, picture_t *picture)
{
    vout_display_sys_t *sys = vd->sys;
370

371
372
373
374
    /* swap the two Y offsets if the drivers supports panning */
    if (sys->has_pan) {
        sys->var_info.yoffset = 0;
        /*vd->sys->var_info.yoffset = vd->sys->var_info.yres; */
375

376
377
378
        /* the X offset should be 0, but who knows ...
         * some other app might have played with the framebuffer */
        sys->var_info.xoffset = 0;
Laurent Aimar's avatar
Laurent Aimar committed
379

380
381
382
383
384
385
        /* FIXME 'static' is damn wrong and it's dead code ... */
        static int panned = 0;
        if (panned < 0) {
            ioctl(sys->fd, FBIOPAN_DISPLAY, &sys->var_info);
            panned++;
        }
Laurent Aimar's avatar
Laurent Aimar committed
386
387
    }

388
389
    if (!sys->is_hw_accel)
        picture_Copy(sys->picture, picture);
Laurent Aimar's avatar
Laurent Aimar committed
390

391
    picture_Release(picture);
Laurent Aimar's avatar
Laurent Aimar committed
392
}
Laurent Aimar's avatar
Laurent Aimar committed
393
static int Control(vout_display_t *vd, int query, va_list args)
Laurent Aimar's avatar
Laurent Aimar committed
394
{
395
    vout_display_sys_t *sys = vd->sys;
Laurent Aimar's avatar
Laurent Aimar committed
396

Laurent Aimar's avatar
Laurent Aimar committed
397
    switch (query) {
398
399
400
401
402
403
    case VOUT_DISPLAY_CHANGE_DISPLAY_SIZE: {
        const vout_display_cfg_t *cfg = va_arg(args, const vout_display_cfg_t *);
        if (cfg->display.width  != sys->width ||
            cfg->display.height != sys->height)
            return VLC_EGENERIC;
        return VLC_SUCCESS;
404
    }
405
406
    default:
        msg_Err(vd, "Unsupported query in vout display fb");
407
408
        return VLC_EGENERIC;
    }
409
410
411
412
413
414
415
416
417
}
static void Manage (vout_display_t *vd)
{
    VLC_UNUSED(vd);
#if 0
    /*
     * Size change
     */
    if (vd->i_changes & VOUT_SIZE_CHANGE)
418
    {
419
420
        msg_Dbg(vd, "reinitializing framebuffer screen");
        vd->i_changes &= ~VOUT_SIZE_CHANGE;
421

422
        vout_display_SendEventDisplaySize();
423

424
        ClearScreen(vd->sys);
425
    }
426
#endif
427
428
}

429
430
/* following functions are local */
static int TtyInit(vout_display_t *vd)
431
{
432
    vout_display_sys_t *sys = vd->sys;
433

434
    struct termios new_termios;
435

436
    GfxMode(sys->tty);
437

438
439
440
    /* Set keyboard settings */
    if (tcgetattr(0, &sys->old_termios) == -1) {
        msg_Err(vd, "tcgetattr failed");
441
    }
442

443
444
    if (tcgetattr(0, &new_termios) == -1) {
        msg_Err(vd, "tcgetattr failed");
445
446
    }

447
448
449
450
451
452
453
    /* new_termios.c_lflag &= ~ (ICANON | ISIG);
    new_termios.c_lflag |= (ECHO | ECHOCTL); */
    new_termios.c_lflag &= ~ (ICANON);
    new_termios.c_lflag &= ~(ECHO | ECHOCTL);
    new_termios.c_iflag = 0;
    new_termios.c_cc[VMIN] = 1;
    new_termios.c_cc[VTIME] = 0;
454

455
456
    if (tcsetattr(0, TCSAFLUSH, &new_termios) == -1) {
        msg_Err(vd, "tcsetattr failed");
457
    }
458

459
    ioctl(sys->tty, VT_RELDISP, VT_ACKACQ);
460

461
462
463
464
465
466
467
468
469
470
    /* Set-up tty signal handler to be aware of tty changes */
    struct sigaction sig_tty;
    memset(&sig_tty, 0, sizeof(sig_tty));
    sig_tty.sa_handler = SwitchDisplay;
    sigemptyset(&sig_tty.sa_mask);
    if (sigaction(SIGUSR1, &sig_tty, &sys->sig_usr1) ||
        sigaction(SIGUSR2, &sig_tty, &sys->sig_usr2)) {
        msg_Err(vd, "cannot set signal handler (%m)");
        /* FIXME SIGUSR1 could have succeed */
        goto error_signal;
471
    }
472

473
474
475
476
    /* Set-up tty according to new signal handler */
    if (-1 == ioctl(sys->tty, VT_GETMODE, &sys->vt_mode)) {
        msg_Err(vd, "cannot get terminal mode (%m)");
        goto error;
477
    }
478
479
480
481
482
    struct vt_mode vt_mode = sys->vt_mode;
    vt_mode.mode   = VT_PROCESS;
    vt_mode.waitv  = 0;
    vt_mode.relsig = SIGUSR1;
    vt_mode.acqsig = SIGUSR2;
483

484
485
486
    if (-1 == ioctl(sys->tty, VT_SETMODE, &vt_mode)) {
        msg_Err(vd, "cannot set terminal mode (%m)");
        goto error;
487
    }
488
    return VLC_SUCCESS;
489

490
491
492
493
494
495
496
497
498
error:
    sigaction(SIGUSR1, &sys->sig_usr1, NULL);
    sigaction(SIGUSR2, &sys->sig_usr2, NULL);
error_signal:
    tcsetattr(0, 0, &sys->old_termios);
    TextMode(sys->tty);
    return VLC_EGENERIC;
}
static void TtyExit(vout_display_t *vd)
499
{
500
    vout_display_sys_t *sys = vd->sys;
Sam Hocevar's avatar
Sam Hocevar committed
501

502
503
    /* Reset the terminal */
    ioctl(sys->tty, VT_SETMODE, &sys->vt_mode);
504

505
506
507
    /* Remove signal handlers */
    sigaction(SIGUSR1, &sys->sig_usr1, NULL);
    sigaction(SIGUSR2, &sys->sig_usr2, NULL);
508

509
510
    /* Reset the keyboard state */
    tcsetattr(0, 0, &sys->old_termios);
511

512
513
    /* Return to text mode */
    TextMode(sys->tty);
514
515
516
517
518
}

/*****************************************************************************
 * OpenDisplay: initialize framebuffer
 *****************************************************************************/
519
static int OpenDisplay(vout_display_t *vd, bool force_resolution)
520
{
521
    vout_display_sys_t *sys = vd->sys;
522
523
524
    char *psz_device;                             /* framebuffer device path */

    /* Open framebuffer device */
525
526
    if (!(psz_device = config_GetPsz(vd, FB_DEV_VAR))) {
        msg_Err(vd, "don't know which fb device to open");
527
        return VLC_EGENERIC;
528
529
    }

530
    sys->fd = utf8_open(psz_device, O_RDWR);
531
532
533
    if (sys->fd == -1) {
        msg_Err(vd, "cannot open %s (%m)", psz_device);
        free(psz_device);
534
        return VLC_EGENERIC;
535
    }
536
    free(psz_device);
537

538
    /* Get framebuffer device information */
539
540
541
    if (ioctl(sys->fd, FBIOGET_VSCREENINFO, &sys->var_info)) {
        msg_Err(vd, "cannot get fb info (%m)");
        close(sys->fd);
542
        return VLC_EGENERIC;
543
    }
544
    sys->old_info = sys->var_info;
545

546
    /* Get some info on the framebuffer itself */
547
548
549
    if (force_resolution) {
        sys->var_info.xres = sys->var_info.xres_virtual = sys->width;
        sys->var_info.yres = sys->var_info.yres_virtual = sys->height;
550
551
    }

552
    /* Set some attributes */
553
554
555
556
557
558
559
560
    sys->var_info.activate = sys->is_tty ? FB_ACTIVATE_NXTOPEN :
                                           FB_ACTIVATE_NOW;
    sys->var_info.xoffset  =  0;
    sys->var_info.yoffset  =  0;

    if (ioctl(sys->fd, FBIOPUT_VSCREENINFO, &sys->var_info)) {
        msg_Err(vd, "cannot set fb info (%m)");
        close(sys->fd);
561
        return VLC_EGENERIC;
562
563
    }

564
    struct fb_fix_screeninfo fix_info;
565
    /* Get some information again, in the definitive configuration */
566
    if (ioctl(sys->fd, FBIOGET_FSCREENINFO, &fix_info) ||
567
568
        ioctl(sys->fd, FBIOGET_VSCREENINFO, &sys->var_info)) {
        msg_Err(vd, "cannot get additional fb info (%m)");
569
570

        /* Restore fb config */
571
        ioctl(sys->fd, FBIOPUT_VSCREENINFO, &sys->old_info);
572

573
        close(sys->fd);
574
        return VLC_EGENERIC;
575
576
    }

577
578
    /* If the fb has limitations on mode change,
     * then keep the resolution of the fb */
579
580
581
582
583
    if ((sys->height != sys->var_info.yres) ||
        (sys->width != sys->var_info.xres)) {
        msg_Warn(vd,
                 "using framebuffer native resolution instead of requested (%ix%i)",
                 sys->width, sys->height);
584
    }
585
586
587
    sys->height = sys->var_info.yres;
    sys->width  = sys->var_info.xres_virtual ? sys->var_info.xres_virtual :
                                               sys->var_info.xres;
588
    sys->line_length = fix_info.line_length;
589

590
591
    /* FIXME: if the image is full-size, it gets cropped on the left
     * because of the xres / xres_virtual slight difference */
592
593
594
595
596
    msg_Dbg(vd, "%ix%i (virtual %ix%i) (request %ix%i)",
            sys->var_info.xres, sys->var_info.yres,
            sys->var_info.xres_virtual,
            sys->var_info.yres_virtual,
            sys->width, sys->height);
597

598
    sys->palette = NULL;
599
    sys->has_pan = (fix_info.ypanstep || fix_info.ywrapstep);
600

601
    switch (sys->var_info.bits_per_pixel) {
602
    case 8:
603
604
        sys->palette = malloc(8 * 256 * sizeof(uint16_t));
        if (!sys->palette) {
605
            /* Restore fb config */
606
            ioctl(sys->fd, FBIOPUT_VSCREENINFO, &sys->old_info);
607

608
            close(sys->fd);
609
610
            return VLC_ENOMEM;
        }
611
612
613
614
615
616
        sys->fb_cmap.start = 0;
        sys->fb_cmap.len = 256;
        sys->fb_cmap.red = sys->palette;
        sys->fb_cmap.green = sys->palette + 256 * sizeof(uint16_t);
        sys->fb_cmap.blue = sys->palette + 2 * 256 * sizeof(uint16_t);
        sys->fb_cmap.transp = sys->palette + 3 * 256 * sizeof(uint16_t);
617
618

        /* Save the colormap */
619
        ioctl(sys->fd, FBIOGETCMAP, &sys->fb_cmap);
620

621
        sys->bytes_per_pixel = 1;
622
623
624
625
        break;

    case 15:
    case 16:
626
        sys->bytes_per_pixel = 2;
627
628
629
        break;

    case 24:
630
        sys->bytes_per_pixel = 3;
631
632
633
        break;

    case 32:
634
        sys->bytes_per_pixel = 4;
635
636
637
        break;

    default:
638
639
        msg_Err(vd, "screen depth %d is not supported",
                sys->var_info.bits_per_pixel);
640
641

        /* Restore fb config */
642
        ioctl(sys->fd, FBIOPUT_VSCREENINFO, &sys->old_info);
643

644
        close(sys->fd);
645
        return VLC_EGENERIC;
646
647
    }

648
    sys->video_size = sys->line_length * sys->var_info.yres_virtual;
649
650

    /* Map a framebuffer at the beginning */
651
652
    sys->video_ptr = mmap(NULL, sys->video_size,
                          PROT_READ | PROT_WRITE, MAP_SHARED, sys->fd, 0);
653

654
655
    if (sys->video_ptr == MAP_FAILED) {
        msg_Err(vd, "cannot map video memory (%m)");
656

657
658
659
        if (sys->var_info.bits_per_pixel == 8) {
            free(sys->palette);
            sys->palette = NULL;
660
661
662
        }

        /* Restore fb config */
663
        ioctl(sys->fd, FBIOPUT_VSCREENINFO, &sys->old_info);
664

665
        close(sys->fd);
666
        return VLC_EGENERIC;
667
    }
668

669
    ClearScreen(sys);
670

671
672
    msg_Dbg(vd,
            "framebuffer type=%d, visual=%d, ypanstep=%d, ywrap=%d, accel=%d",
673
674
            fix_info.type, fix_info.visual,
            fix_info.ypanstep, fix_info.ywrapstep, fix_info.accel);
675
    return VLC_SUCCESS;
676
677
678
679
680
}

/*****************************************************************************
 * CloseDisplay: terminate FB video thread output method
 *****************************************************************************/
681
static void CloseDisplay(vout_display_t *vd)
682
{
683
684
685
    vout_display_sys_t *sys = vd->sys;

    if (sys->video_ptr != MAP_FAILED) {
686
        ClearScreen(sys);
687
        munmap(sys->video_ptr, sys->video_size);
688
    }
689

690
    if (sys->fd >= 0) {
691
        /* Restore palette */
692
693
694
695
        if (sys->var_info.bits_per_pixel == 8) {
            ioctl(sys->fd, FBIOPUTCMAP, &sys->fb_cmap);
            free(sys->palette);
            sys->palette = NULL;
696
        }
697

698
        /* Restore fb config */
699
        ioctl(sys->fd, FBIOPUT_VSCREENINFO, &sys->old_info);
700

701
        /* Close fb */
702
        close(sys->fd);
703
    }
704
705
706
707
708
709
710
711
}

/*****************************************************************************
 * SwitchDisplay: VT change signal handler
 *****************************************************************************
 * This function activates or deactivates the output of the thread. It is
 * called by the VT driver, on terminal change.
 *****************************************************************************/
712
static void SwitchDisplay(int i_signal)
713
{
714
    VLC_UNUSED(i_signal);
715
#if 0
716
    vout_display_t *vd;
717

718
    vlc_mutex_lock(&p_vout_bank->lock);
719
720

    /* XXX: only test the first video output */
721
    if (p_vout_bank->i_count)
722
    {
723
        vd = p_vout_bank->pp_vout[0];
724

725
        switch (i_signal)
726
727
        {
        case SIGUSR1:                                /* vt has been released */
728
729
            vd->b_active = 0;
            ioctl(sys->tty, VT_RELDISP, 1);
730
731
            break;
        case SIGUSR2:                                /* vt has been acquired */
732
733
            vd->b_active = 1;
            ioctl(sys->tty, VT_RELDISP, VT_ACTIVATE);
734
            /* handle blanking */
735
736
737
            vlc_mutex_lock(&vd->change_lock);
            vd->i_changes |= VOUT_SIZE_CHANGE;
            vlc_mutex_unlock(&vd->change_lock);
738
739
740
741
            break;
        }
    }

742
    vlc_mutex_unlock(&p_vout_bank->lock);
743
744
745
746
747
748
749
750
#endif
}

/*****************************************************************************
 * TextMode and GfxMode : switch tty to text/graphic mode
 *****************************************************************************
 * These functions toggle the tty mode.
 *****************************************************************************/
751
static void TextMode(int tty)
752
753
{
    /* return to text mode */
754
755
    if (-1 == ioctl(tty, KDSETMODE, KD_TEXT)) {
        /*msg_Err(vd, "failed ioctl KDSETMODE KD_TEXT");*/
756
757
758
    }
}

759
static void GfxMode(int tty)
760
761
{
    /* switch to graphic mode */
762
763
    if (-1 == ioctl(tty, KDSETMODE, KD_GRAPHICS)) {
        /*msg_Err(vd, "failed ioctl KDSETMODE KD_GRAPHICS");*/
764
765
    }
}