Remove stray semicolon.
[vlc.git] / modules / video_output / fb.c
1 /*****************************************************************************
2  * fb.c : framebuffer plugin for vlc
3  *****************************************************************************
4  * Copyright (C) 2000, 2001 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Samuel Hocevar <sam@zoy.org>
8  *          Jean-Paul Saman
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23  *****************************************************************************/
24
25 /*****************************************************************************
26  * Preamble
27  *****************************************************************************/
28 #include <errno.h>                                                 /* ENOMEM */
29 #include <signal.h>                                      /* SIGUSR1, SIGUSR2 */
30 #include <fcntl.h>                                                 /* open() */
31 #include <unistd.h>                                               /* close() */
32
33 #include <termios.h>                                       /* struct termios */
34 #include <sys/ioctl.h>
35 #include <sys/mman.h>                                              /* mmap() */
36
37 #include <linux/fb.h>
38 #include <linux/vt.h>                                                /* VT_* */
39 #include <linux/kd.h>                                                 /* KD* */
40
41 #include <vlc/vlc.h>
42 #include <vlc_vout.h>
43
44 /*****************************************************************************
45  * Local prototypes
46  *****************************************************************************/
47 static int  Create    ( vlc_object_t * );
48 static void Destroy   ( vlc_object_t * );
49
50 static int  Init      ( vout_thread_t * );
51 static void End       ( vout_thread_t * );
52 static int  Manage    ( vout_thread_t * );
53 static void Display   ( vout_thread_t *, picture_t * );
54 static int  Control   ( vout_thread_t *, int, va_list );
55
56 static int  OpenDisplay    ( vout_thread_t * );
57 static void CloseDisplay   ( vout_thread_t * );
58 static void SwitchDisplay  ( int i_signal );
59 static void TextMode       ( int i_tty );
60 static void GfxMode        ( int i_tty );
61
62 /*****************************************************************************
63  * Module descriptor
64  *****************************************************************************/
65 #define FB_DEV_VAR "fbdev"
66
67 #define DEVICE_TEXT N_("Framebuffer device")
68 #define DEVICE_LONGTEXT N_( \
69     "Framebuffer device to use for rendering (usually /dev/fb0).")
70
71 #define TTY_TEXT N_("Run fb on current tty.")
72 #define TTY_LONGTEXT N_( \
73     "Run framebuffer on current TTY device (default enabled). " \
74     "(disable tty handling with caution)" )
75
76 #define CHROMA_TEXT N_("Chroma used.")
77 #define CHROMA_LONGTEXT N_( \
78     "Force use of a specific chroma for output. Default is I420." )
79
80 #define ASPECT_RATIO_TEXT N_("Video aspect ratio")
81 #define ASPECT_RATIO_LONGTEXT N_( \
82     "Aspect ratio of the video image (4:3, 16:9). Default is square pixels." )
83
84 #define FB_MODE_TEXT N_("Framebuffer resolution to use.")
85 #define FB_MODE_LONGTEXT N_( \
86     "Select the resolution for the framebuffer. Currently it supports " \
87     "the values 0=QCIF 1=CIF 2=NTSC 3=PAL, 4=auto (default 4=auto)" )
88
89 vlc_module_begin();
90     set_shortname( "Framebuffer" );
91     set_category( CAT_VIDEO );
92     set_subcategory( SUBCAT_VIDEO_VOUT );
93     add_file( FB_DEV_VAR, "/dev/fb0", NULL, DEVICE_TEXT, DEVICE_LONGTEXT,
94               VLC_FALSE );
95     add_bool( "fb-tty", 1, NULL, TTY_TEXT, TTY_LONGTEXT, VLC_TRUE );
96     add_string( "fb-chroma", NULL, NULL, CHROMA_TEXT, CHROMA_LONGTEXT,
97                 VLC_TRUE );
98     add_string( "fb-aspect-ratio", NULL, NULL, ASPECT_RATIO_TEXT,
99                 ASPECT_RATIO_LONGTEXT, VLC_TRUE );
100     add_integer( "fb-mode", 4, NULL, FB_MODE_TEXT, FB_MODE_LONGTEXT,
101                  VLC_TRUE );
102     set_description( _("GNU/Linux console framebuffer video output") );
103     set_capability( "video output", 30 );
104     set_callbacks( Create, Destroy );
105 vlc_module_end();
106
107 /*****************************************************************************
108  * vout_sys_t: video output framebuffer method descriptor
109  *****************************************************************************
110  * This structure is part of the video output thread descriptor.
111  * It describes the FB specific properties of an output thread.
112  *****************************************************************************/
113 struct vout_sys_t
114 {
115     /* System information */
116     int                 i_tty;                          /* tty device handle */
117     vlc_bool_t          b_tty;
118     struct termios      old_termios;
119
120     /* Original configuration information */
121     struct sigaction            sig_usr1;           /* USR1 previous handler */
122     struct sigaction            sig_usr2;           /* USR2 previous handler */
123     struct vt_mode              vt_mode;                 /* previous VT mode */
124
125     /* Framebuffer information */
126     int                         i_fd;                       /* device handle */
127     struct fb_var_screeninfo    old_info;       /* original mode information */
128     struct fb_var_screeninfo    var_info;        /* current mode information */
129     vlc_bool_t                  b_pan;     /* does device supports panning ? */
130     struct fb_cmap              fb_cmap;                /* original colormap */
131     uint16_t                    *p_palette;              /* original palette */
132
133     /* Video information */
134     uint32_t i_width;
135     uint32_t i_height;
136     int i_aspect;
137     int i_bytes_per_pixel;
138     vlc_bool_t   b_auto;       /* Automatically adjust video size to fb size */
139     vlc_fourcc_t i_chroma;
140
141     /* Video memory */
142     byte_t *    p_video;                                      /* base adress */
143     size_t      i_page_size;                                    /* page size */
144 };
145
146 /*****************************************************************************
147  * Create: allocates FB video thread output method
148  *****************************************************************************
149  * This function allocates and initializes a FB vout method.
150  *****************************************************************************/
151 static int Create( vlc_object_t *p_this )
152 {
153     vout_thread_t *p_vout = (vout_thread_t *)p_this;
154     vout_sys_t    *p_sys;
155     char          *psz_chroma;
156     char          *psz_aspect;
157     int           i_mode;
158     struct sigaction    sig_tty;                 /* sigaction for tty change */
159     struct vt_mode      vt_mode;                          /* vt current mode */
160     struct termios      new_termios;
161
162     /* Allocate instance and initialize some members */
163     p_vout->p_sys = p_sys = malloc( sizeof( vout_sys_t ) );
164     if( p_vout->p_sys == NULL )
165     {
166         msg_Err( p_vout, "out of memory" );
167         return VLC_ENOMEM;
168     };
169     memset( p_sys, 0, sizeof(vout_sys_t) );
170
171     p_vout->pf_init = Init;
172     p_vout->pf_end = End;
173     p_vout->pf_manage = Manage;
174     p_vout->pf_render = NULL;
175     p_vout->pf_display = Display;
176     p_vout->pf_control = Control;
177
178     /* Set tty and fb devices */
179     p_sys->i_tty = 0; /* 0 == /dev/tty0 == current console */
180     p_sys->b_tty = var_CreateGetBool( p_vout, "fb-tty" );
181 #ifndef WIN32
182 #if defined(HAVE_ISATTY)
183     /* Check that stdin is a TTY */
184     if( p_sys->b_tty && !isatty( 0 ) )
185     {
186         msg_Warn( p_vout, "fd 0 is not a TTY" );
187         return VLC_EGENERIC;
188     }
189     else
190     {
191         msg_Warn( p_vout, "disabling tty handling, use with caution because "
192                           "there is no way to return to the tty." );
193     }
194 #endif
195 #endif
196
197     psz_chroma = var_CreateGetNonEmptyString( p_vout, "fb-chroma" );
198     if( psz_chroma )
199     {
200         if( strlen( psz_chroma ) == 4 )
201         {
202             p_sys->i_chroma = VLC_FOURCC( psz_chroma[0],
203                                    psz_chroma[1],
204                                    psz_chroma[2],
205                                    psz_chroma[3] );
206             msg_Dbg( p_vout, "forcing chroma '%s'", psz_chroma );
207         }
208         else
209         {
210             msg_Warn( p_vout, "invalid chroma (%s), using defaults.",
211                       psz_chroma );
212         }
213         free( psz_chroma );
214         psz_chroma = NULL;
215     }
216
217     p_sys->i_aspect = -1;
218     psz_aspect = var_CreateGetNonEmptyString( p_vout, "fb-aspect-ratio" );
219     if( psz_aspect )
220     {
221         char *psz_parser = strchr( psz_aspect, ':' );
222
223         if( psz_parser )
224         {
225             *psz_parser++ = '\0';
226             p_sys->i_aspect = ( atoi( psz_aspect )
227                               * VOUT_ASPECT_FACTOR ) / atoi( psz_parser );
228         }
229         msg_Dbg( p_vout, "using aspect ratio %d:%d",
230                   atoi( psz_aspect ), atoi( psz_parser ) );
231
232         free( psz_aspect );
233         psz_aspect = NULL;
234     }
235
236     p_sys->b_auto = VLC_FALSE;
237     i_mode = var_CreateGetInteger( p_vout, "fb-mode" );
238     switch( i_mode )
239     {
240         case 0: /* QCIF */
241             p_sys->i_width  = 176;
242             p_sys->i_height = 144;
243             break;
244         case 1: /* CIF */
245             p_sys->i_width  = 352;
246             p_sys->i_height = 288;
247             break;
248         case 2: /* NTSC */
249             p_sys->i_width  = 640;
250             p_sys->i_height = 480;
251             break;
252         case 3: /* PAL */
253             p_sys->i_width  = 704;
254             p_sys->i_height = 576;
255             break;
256         case 4:
257         default:
258             p_sys->b_auto = VLC_TRUE;
259             break;
260      }
261
262     /* tty handling */
263     if( p_sys->b_tty )
264     {
265         GfxMode( p_sys->i_tty );
266
267         /* Set keyboard settings */
268         if( tcgetattr(0, &p_vout->p_sys->old_termios) == -1 )
269         {
270             msg_Err( p_vout, "tcgetattr failed" );
271         }
272
273         if( tcgetattr(0, &new_termios) == -1 )
274         {
275             msg_Err( p_vout, "tcgetattr failed" );
276         }
277
278         /* new_termios.c_lflag &= ~ (ICANON | ISIG);
279         new_termios.c_lflag |= (ECHO | ECHOCTL); */
280         new_termios.c_lflag &= ~ (ICANON);
281         new_termios.c_lflag &= ~(ECHO | ECHOCTL);
282         new_termios.c_iflag = 0;
283         new_termios.c_cc[VMIN] = 1;
284         new_termios.c_cc[VTIME] = 0;
285
286         if( tcsetattr(0, TCSAFLUSH, &new_termios) == -1 )
287         {
288             msg_Err( p_vout, "tcsetattr failed" );
289         }
290
291         ioctl( p_sys->i_tty, VT_RELDISP, VT_ACKACQ );
292
293         /* Set-up tty signal handler to be aware of tty changes */
294         memset( &sig_tty, 0, sizeof( sig_tty ) );
295         sig_tty.sa_handler = SwitchDisplay;
296         sigemptyset( &sig_tty.sa_mask );
297         if( sigaction( SIGUSR1, &sig_tty, &p_vout->p_sys->sig_usr1 ) ||
298             sigaction( SIGUSR2, &sig_tty, &p_vout->p_sys->sig_usr2 ) )
299         {
300             msg_Err( p_vout, "cannot set signal handler (%m)" );
301             tcsetattr(0, 0, &p_vout->p_sys->old_termios);
302             TextMode( p_sys->i_tty );
303             free( p_vout->p_sys );
304             return VLC_EGENERIC;
305         }
306
307         /* Set-up tty according to new signal handler */
308         if( -1 == ioctl( p_sys->i_tty, VT_GETMODE, &p_vout->p_sys->vt_mode ) )
309         {
310             msg_Err( p_vout, "cannot get terminal mode (%m)" );
311             sigaction( SIGUSR1, &p_vout->p_sys->sig_usr1, NULL );
312             sigaction( SIGUSR2, &p_vout->p_sys->sig_usr2, NULL );
313             tcsetattr(0, 0, &p_vout->p_sys->old_termios);
314             TextMode( p_sys->i_tty );
315             free( p_vout->p_sys );
316             return VLC_EGENERIC;
317         }
318         memcpy( &vt_mode, &p_vout->p_sys->vt_mode, sizeof( vt_mode ) );
319         vt_mode.mode   = VT_PROCESS;
320         vt_mode.waitv  = 0;
321         vt_mode.relsig = SIGUSR1;
322         vt_mode.acqsig = SIGUSR2;
323
324         if( -1 == ioctl( p_sys->i_tty, VT_SETMODE, &vt_mode ) )
325         {
326             msg_Err( p_vout, "cannot set terminal mode (%m)" );
327             sigaction( SIGUSR1, &p_vout->p_sys->sig_usr1, NULL );
328             sigaction( SIGUSR2, &p_vout->p_sys->sig_usr2, NULL );
329             tcsetattr(0, 0, &p_vout->p_sys->old_termios);
330             TextMode( p_sys->i_tty );
331             free( p_vout->p_sys );
332             return VLC_EGENERIC;
333         }
334     }
335
336     if( OpenDisplay( p_vout ) )
337     {
338         if( p_sys->b_tty )
339         {
340             ioctl( p_sys->i_tty, VT_SETMODE, &p_vout->p_sys->vt_mode );
341             sigaction( SIGUSR1, &p_vout->p_sys->sig_usr1, NULL );
342             sigaction( SIGUSR2, &p_vout->p_sys->sig_usr2, NULL );
343             tcsetattr(0, 0, &p_vout->p_sys->old_termios);
344             TextMode( p_sys->i_tty );
345         }
346         free( p_vout->p_sys );
347         return VLC_EGENERIC;
348     }
349
350     return VLC_SUCCESS;
351 }
352
353 /*****************************************************************************
354  * Init: initialize framebuffer video thread output method
355  *****************************************************************************/
356 static int Init( vout_thread_t *p_vout )
357 {
358     vout_sys_t *p_sys = p_vout->p_sys;
359     int i_index;
360     picture_t *p_pic;
361
362     I_OUTPUTPICTURES = 0;
363
364     if( p_sys->i_chroma == 0 )
365     {
366         /* Initialize the output structure: RGB with square pixels, whatever
367          * the input format is, since it's the only format we know */
368         switch( p_sys->var_info.bits_per_pixel )
369         {
370         case 8: /* FIXME: set the palette */
371             p_vout->output.i_chroma = VLC_FOURCC('R','G','B','2'); break;
372         case 15:
373             p_vout->output.i_chroma = VLC_FOURCC('R','V','1','5'); break;
374         case 16:
375             p_vout->output.i_chroma = VLC_FOURCC('R','V','1','6'); break;
376         case 24:
377             p_vout->output.i_chroma = VLC_FOURCC('R','V','2','4'); break;
378         case 32:
379             p_vout->output.i_chroma = VLC_FOURCC('R','V','3','2'); break;
380         default:
381             msg_Err( p_vout, "unknown screen depth %i",
382                      p_vout->p_sys->var_info.bits_per_pixel );
383             return VLC_EGENERIC;
384         }
385
386         if( p_sys->var_info.bits_per_pixel != 8 )
387         {
388             p_vout->output.i_rmask = ( (1 << p_sys->var_info.red.length) - 1 )
389                                  << p_sys->var_info.red.offset;
390             p_vout->output.i_gmask = ( (1 << p_sys->var_info.green.length) - 1 )
391                                  << p_sys->var_info.green.offset;
392             p_vout->output.i_bmask = ( (1 << p_sys->var_info.blue.length) - 1 )
393                                  << p_sys->var_info.blue.offset;
394         }
395     }
396     else
397     {
398         p_vout->output.i_chroma = p_sys->i_chroma;
399     }
400     p_vout->fmt_out.i_chroma = p_vout->output.i_chroma;
401
402     if( !p_sys->b_auto )
403     {
404         p_vout->render.i_width = p_sys->i_width;
405         p_vout->render.i_height = p_sys->i_height;
406     }
407     p_vout->output.i_width  = p_sys->i_width;
408     p_vout->output.i_height = p_sys->i_height;
409
410     /* Assume we have square pixels */
411     if( p_sys->i_aspect < 0 )
412     {
413         p_vout->output.i_aspect = ( p_sys->i_width
414                                   * VOUT_ASPECT_FACTOR ) / p_sys->i_height;
415     }
416     else p_vout->output.i_aspect = p_sys->i_aspect;
417
418     p_vout->fmt_out.i_sar_num = p_vout->fmt_out.i_sar_den = 1;
419     p_vout->fmt_out.i_aspect  = p_vout->render.i_aspect = p_vout->output.i_aspect;
420
421     /* Clear the screen */
422     memset( p_sys->p_video, 0, p_sys->i_page_size );
423
424     /* Try to initialize 1 direct buffer */
425     p_pic = NULL;
426
427     /* Find an empty picture slot */
428     for( i_index = 0 ; i_index < VOUT_MAX_PICTURES ; i_index++ )
429     {
430         if( p_vout->p_picture[ i_index ].i_status == FREE_PICTURE )
431         {
432             p_pic = p_vout->p_picture + i_index;
433             break;
434         }
435     }
436
437     /* Allocate the picture */
438     if( p_pic == NULL )
439     {
440         return VLC_EGENERIC;
441     }
442
443     /* We know the chroma, allocate a buffer which will be used
444      * directly by the decoder */
445     p_pic->p->p_pixels = p_vout->p_sys->p_video;
446     p_pic->p->i_pixel_pitch = p_vout->p_sys->i_bytes_per_pixel;
447     p_pic->p->i_lines = p_vout->p_sys->var_info.yres;
448     p_pic->p->i_visible_lines = p_vout->p_sys->var_info.yres;
449
450     if( p_vout->p_sys->var_info.xres_virtual )
451     {
452         p_pic->p->i_pitch = p_vout->p_sys->var_info.xres_virtual
453                              * p_vout->p_sys->i_bytes_per_pixel;
454     }
455     else
456     {
457         p_pic->p->i_pitch = p_vout->p_sys->var_info.xres
458                              * p_vout->p_sys->i_bytes_per_pixel;
459     }
460
461     p_pic->p->i_visible_pitch = p_vout->p_sys->var_info.xres
462                                  * p_vout->p_sys->i_bytes_per_pixel;
463     p_pic->i_planes = 1;
464     p_pic->i_status = DESTROYED_PICTURE;
465     p_pic->i_type   = DIRECT_PICTURE;
466
467     PP_OUTPUTPICTURE[ I_OUTPUTPICTURES ] = p_pic;
468
469     I_OUTPUTPICTURES++;
470
471     return VLC_SUCCESS;
472 }
473
474 /*****************************************************************************
475  * End: terminate framebuffer video thread output method
476  *****************************************************************************/
477 static void End( vout_thread_t *p_vout )
478 {
479     /* Clear the screen */
480     memset( p_vout->p_sys->p_video, 0, p_vout->p_sys->i_page_size );
481 }
482
483 /*****************************************************************************
484  * Destroy: destroy FB video thread output method
485  *****************************************************************************
486  * Terminate an output method created by Create
487  *****************************************************************************/
488 static void Destroy( vlc_object_t *p_this )
489 {
490     vout_thread_t *p_vout = (vout_thread_t *)p_this;
491
492     CloseDisplay( p_vout );
493
494     if( p_vout->p_sys->b_tty )
495     {
496         /* Reset the terminal */
497         ioctl( p_vout->p_sys->i_tty, VT_SETMODE, &p_vout->p_sys->vt_mode );
498
499         /* Remove signal handlers */
500         sigaction( SIGUSR1, &p_vout->p_sys->sig_usr1, NULL );
501         sigaction( SIGUSR2, &p_vout->p_sys->sig_usr2, NULL );
502
503         /* Reset the keyboard state */
504         tcsetattr( 0, 0, &p_vout->p_sys->old_termios );
505
506         /* Return to text mode */
507         TextMode( p_vout->p_sys->i_tty );
508     }
509
510     /* Destroy structure */
511     free( p_vout->p_sys );
512 }
513
514 /*****************************************************************************
515  * Control: control facility for the vout
516  *****************************************************************************/
517 static int Control( vout_thread_t *p_vout, int i_query, va_list args )
518 {
519     switch( i_query )
520     {
521        default:
522             return vout_vaControlDefault( p_vout, i_query, args );
523     }
524 }
525
526 /*****************************************************************************
527  * Manage: handle FB events
528  *****************************************************************************
529  * This function should be called regularly by video output thread. It manages
530  * console events. It returns a non null value on error.
531  *****************************************************************************/
532 static int Manage( vout_thread_t *p_vout )
533 {
534 #if 0
535     uint8_t buf;
536
537     if ( read(0, &buf, 1) == 1)
538     {
539         switch( buf )
540         {
541         case 'q':
542             vlc_object_kill( p_vout->p_libvlc );
543             break;
544
545         default:
546             break;
547         }
548     }
549 #endif
550
551     /*
552      * Size change
553      */
554     if( p_vout->i_changes & VOUT_SIZE_CHANGE )
555     {
556         msg_Dbg( p_vout, "reinitializing framebuffer screen" );
557         p_vout->i_changes &= ~VOUT_SIZE_CHANGE;
558
559         /* Destroy XImages to change their size */
560         End( p_vout );
561
562         /* Recreate XImages. If SysInit failed, the thread can't go on. */
563         if( Init( p_vout ) )
564         {
565             msg_Err( p_vout, "cannot reinit framebuffer screen" );
566             return VLC_EGENERIC;
567         }
568
569         /* Clear screen */
570         memset( p_vout->p_sys->p_video, 0, p_vout->p_sys->i_page_size );
571
572 #if 0
573         /* Tell the video output thread that it will need to rebuild YUV
574          * tables. This is needed since conversion buffer size may have changed */
575         p_vout->i_changes |= VOUT_YUV_CHANGE;
576 #endif
577     }
578
579     return VLC_SUCCESS;
580 }
581
582 /*****************************************************************************
583  * Display: displays previously rendered output
584  *****************************************************************************
585  * This function send the currently rendered image to FB image, waits until
586  * it is displayed and switch the two rendering buffers, preparing next frame.
587  *****************************************************************************/
588 static void Display( vout_thread_t *p_vout, picture_t *p_pic )
589 {
590 static int panned=0;
591     /* swap the two Y offsets if the drivers supports panning */
592     if( p_vout->p_sys->b_pan )
593     {
594         p_vout->p_sys->var_info.yoffset = 0;
595         /*p_vout->p_sys->var_info.yoffset = p_vout->p_sys->var_info.yres; */
596
597         /* the X offset should be 0, but who knows ...
598          * some other app might have played with the framebuffer */
599         p_vout->p_sys->var_info.xoffset = 0;
600
601         if( panned < 0 )
602         {
603             ioctl( p_vout->p_sys->i_fd,
604                    FBIOPAN_DISPLAY, &p_vout->p_sys->var_info );
605             panned++;
606         }
607     }
608 }
609
610 #if 0
611 static void SetPalette( vout_thread_t *p_vout, uint16_t *red, uint16_t *green,
612                                                uint16_t *blue, uint16_t *transp )
613 {
614     struct fb_cmap cmap = { 0, 256, red, green, blue, transp };
615
616     ioctl( p_vout->p_sys->i_fd, FBIOPUTCMAP, &cmap );
617 }
618 #endif
619
620 /* following functions are local */
621
622 /*****************************************************************************
623  * OpenDisplay: initialize framebuffer
624  *****************************************************************************/
625 static int OpenDisplay( vout_thread_t *p_vout )
626 {
627     vout_sys_t *p_sys = (vout_sys_t *) p_vout->p_sys;
628     char *psz_device;                             /* framebuffer device path */
629     struct fb_fix_screeninfo    fix_info;     /* framebuffer fix information */
630
631     /* Open framebuffer device */
632     if( !(psz_device = config_GetPsz( p_vout, FB_DEV_VAR )) )
633     {
634         msg_Err( p_vout, "don't know which fb device to open" );
635         return VLC_EGENERIC;
636     }
637
638     p_sys->i_fd = open( psz_device, O_RDWR);
639     if( p_sys->i_fd == -1 )
640     {
641         msg_Err( p_vout, "cannot open %s (%m)", psz_device );
642         free( psz_device );
643         return VLC_EGENERIC;
644     }
645     free( psz_device );
646     psz_device = NULL;
647
648     /* Get framebuffer device information */
649     if( ioctl( p_sys->i_fd, FBIOGET_VSCREENINFO, &p_sys->var_info ) )
650     {
651         msg_Err( p_vout, "cannot get fb info (%m)" );
652         close( p_sys->i_fd );
653         return VLC_EGENERIC;
654     }
655
656     memcpy( &p_sys->old_info, &p_sys->var_info,
657             sizeof( struct fb_var_screeninfo ) );
658
659     /* Get some info on the framebuffer itself */
660     if( !p_sys->b_auto )
661     {
662         p_sys->var_info.xres = p_sys->var_info.xres_virtual = p_sys->i_width;
663         p_sys->var_info.yres = p_sys->var_info.yres_virtual = p_sys->i_height;
664         p_vout->fmt_out.i_width = p_sys->i_width;
665         p_vout->fmt_out.i_height = p_sys->i_height;
666     }
667
668     /* Set some attributes */
669     p_sys->var_info.activate = p_sys->b_tty
670                                ? FB_ACTIVATE_NXTOPEN
671                                : FB_ACTIVATE_NOW;
672     p_sys->var_info.xoffset =  0;
673     p_sys->var_info.yoffset =  0;
674
675     if( ioctl( p_sys->i_fd, FBIOPUT_VSCREENINFO, &p_sys->var_info ) )
676     {
677         msg_Err( p_vout, "cannot set fb info (%m)" );
678         close( p_sys->i_fd );
679         return VLC_EGENERIC;
680     }
681
682     /* Get some information again, in the definitive configuration */
683     if( ioctl( p_sys->i_fd, FBIOGET_FSCREENINFO, &fix_info )
684          || ioctl( p_sys->i_fd, FBIOGET_VSCREENINFO, &p_sys->var_info ) )
685     {
686         msg_Err( p_vout, "cannot get additional fb info (%m)" );
687
688         /* Restore fb config */
689         ioctl( p_sys->i_fd, FBIOPUT_VSCREENINFO, &p_sys->old_info );
690
691         close( p_sys->i_fd );
692         return VLC_EGENERIC;
693     }
694
695     /* If the fb has limitations on mode change,
696      * then keep the resolution of the fb */
697     if( (p_sys->i_height != p_sys->var_info.yres) ||
698         (p_sys->i_width != p_sys->var_info.xres) )
699     {
700         p_sys->b_auto = VLC_TRUE;
701         msg_Warn( p_vout,
702                   "using framebuffer native resolution instead of requested (%ix%i)",
703                   p_sys->i_width, p_sys->i_height );
704     }
705     p_sys->i_height = p_sys->var_info.yres;
706     p_sys->i_width  = p_sys->var_info.xres_virtual
707                                ? p_sys->var_info.xres_virtual
708                                : p_sys->var_info.xres;
709
710     /* FIXME: if the image is full-size, it gets cropped on the left
711      * because of the xres / xres_virtual slight difference */
712     msg_Dbg( p_vout, "%ix%i (virtual %ix%i) (request %ix%i)",
713              p_sys->var_info.xres, p_sys->var_info.yres,
714              p_sys->var_info.xres_virtual,
715              p_sys->var_info.yres_virtual,
716              p_sys->i_width, p_sys->i_height );
717
718     p_sys->p_palette = NULL;
719     p_sys->b_pan = ( fix_info.ypanstep || fix_info.ywrapstep );
720
721     switch( p_sys->var_info.bits_per_pixel )
722     {
723     case 8:
724         p_sys->p_palette = malloc( 8 * 256 * sizeof( uint16_t ) );
725         if( !p_sys->p_palette )
726         {
727             msg_Err( p_vout, "out of memory" );
728
729             /* Restore fb config */
730             ioctl( p_sys->i_fd, FBIOPUT_VSCREENINFO, &p_sys->old_info );
731
732             close( p_sys->i_fd );
733             return VLC_ENOMEM;
734         }
735         p_sys->fb_cmap.start = 0;
736         p_sys->fb_cmap.len = 256;
737         p_sys->fb_cmap.red = p_sys->p_palette;
738         p_sys->fb_cmap.green = p_sys->p_palette + 256 * sizeof( uint16_t );
739         p_sys->fb_cmap.blue = p_sys->p_palette + 2 * 256 * sizeof( uint16_t );
740         p_sys->fb_cmap.transp = p_sys->p_palette + 3 * 256 * sizeof( uint16_t );
741
742         /* Save the colormap */
743         ioctl( p_sys->i_fd, FBIOGETCMAP, &p_sys->fb_cmap );
744
745         p_sys->i_bytes_per_pixel = 1;
746         break;
747
748     case 15:
749     case 16:
750         p_sys->i_bytes_per_pixel = 2;
751         break;
752
753     case 24:
754         p_sys->i_bytes_per_pixel = 3;
755         break;
756
757     case 32:
758         p_sys->i_bytes_per_pixel = 4;
759         break;
760
761     default:
762         msg_Err( p_vout, "screen depth %d is not supported",
763                          p_sys->var_info.bits_per_pixel );
764
765         /* Restore fb config */
766         ioctl( p_sys->i_fd, FBIOPUT_VSCREENINFO, &p_sys->old_info );
767
768         close( p_sys->i_fd );
769         return VLC_EGENERIC;
770     }
771
772     p_sys->i_page_size = p_sys->i_width * p_sys->i_height *
773                          p_sys->i_bytes_per_pixel;
774
775     /* Map a framebuffer at the beginning */
776     p_sys->p_video = mmap( 0, p_sys->i_page_size,
777                               PROT_READ | PROT_WRITE, MAP_SHARED,
778                               p_sys->i_fd, 0 );
779
780     if( p_sys->p_video == ((void*)-1) )
781     {
782         msg_Err( p_vout, "cannot map video memory (%m)" );
783
784         if( p_sys->var_info.bits_per_pixel == 8 )
785         {
786             free( p_sys->p_palette );
787             p_sys->p_palette = NULL;
788         }
789
790         /* Restore fb config */
791         ioctl( p_sys->i_fd, FBIOPUT_VSCREENINFO, &p_vout->p_sys->old_info );
792
793         close( p_sys->i_fd );
794         return VLC_EGENERIC;
795     }
796
797     msg_Dbg( p_vout, "framebuffer type=%d, visual=%d, ypanstep=%d, "
798              "ywrap=%d, accel=%d", fix_info.type, fix_info.visual,
799              fix_info.ypanstep, fix_info.ywrapstep, fix_info.accel );
800     return VLC_SUCCESS;
801 }
802
803 /*****************************************************************************
804  * CloseDisplay: terminate FB video thread output method
805  *****************************************************************************/
806 static void CloseDisplay( vout_thread_t *p_vout )
807 {
808     /* Clear display */
809     memset( p_vout->p_sys->p_video, 0, p_vout->p_sys->i_page_size );
810
811     /* Restore palette */
812     if( p_vout->p_sys->var_info.bits_per_pixel == 8 )
813     {
814         ioctl( p_vout->p_sys->i_fd,
815                FBIOPUTCMAP, &p_vout->p_sys->fb_cmap );
816         free( p_vout->p_sys->p_palette );
817         p_vout->p_sys->p_palette = NULL;
818     }
819
820     /* Restore fb config */
821     ioctl( p_vout->p_sys->i_fd,
822            FBIOPUT_VSCREENINFO, &p_vout->p_sys->old_info );
823
824     /* Close fb */
825     close( p_vout->p_sys->i_fd );
826 }
827
828 /*****************************************************************************
829  * SwitchDisplay: VT change signal handler
830  *****************************************************************************
831  * This function activates or deactivates the output of the thread. It is
832  * called by the VT driver, on terminal change.
833  *****************************************************************************/
834 static void SwitchDisplay(int i_signal)
835 {
836 #if 0
837     vout_thread_t *p_vout;
838
839     vlc_mutex_lock( &p_vout_bank->lock );
840
841     /* XXX: only test the first video output */
842     if( p_vout_bank->i_count )
843     {
844         p_vout = p_vout_bank->pp_vout[0];
845
846         switch( i_signal )
847         {
848         case SIGUSR1:                                /* vt has been released */
849             p_vout->b_active = 0;
850             ioctl( p_sys->i_tty, VT_RELDISP, 1 );
851             break;
852         case SIGUSR2:                                /* vt has been acquired */
853             p_vout->b_active = 1;
854             ioctl( p_sys->i_tty, VT_RELDISP, VT_ACTIVATE );
855             /* handle blanking */
856             vlc_mutex_lock( &p_vout->change_lock );
857             p_vout->i_changes |= VOUT_SIZE_CHANGE;
858             vlc_mutex_unlock( &p_vout->change_lock );
859             break;
860         }
861     }
862
863     vlc_mutex_unlock( &p_vout_bank->lock );
864 #endif
865 }
866
867 /*****************************************************************************
868  * TextMode and GfxMode : switch tty to text/graphic mode
869  *****************************************************************************
870  * These functions toggle the tty mode.
871  *****************************************************************************/
872 static void TextMode( int i_tty )
873 {
874     /* return to text mode */
875     if( -1 == ioctl(i_tty, KDSETMODE, KD_TEXT) )
876     {
877         /*msg_Err( p_vout, "failed ioctl KDSETMODE KD_TEXT" );*/
878     }
879 }
880
881 static void GfxMode( int i_tty )
882 {
883     /* switch to graphic mode */
884     if( -1 == ioctl(i_tty, KDSETMODE, KD_GRAPHICS) )
885     {
886         /*msg_Err( p_vout, "failed ioctl KDSETMODE KD_GRAPHICS" );*/
887     }
888 }