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