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