1 /*****************************************************************************
2 * vout_fb.c: framebuffer video output display method
3 *****************************************************************************
4 * Copyright (C) 1998, 1999, 2000, 2001 VideoLAN
6 * Authors: Samuel Hocevar <sam@zoy.org>
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
21 *****************************************************************************/
23 #define MODULE_NAME fb
24 #include "modules_inner.h"
26 /*****************************************************************************
28 *****************************************************************************/
31 #include <errno.h> /* ENOMEM */
32 #include <signal.h> /* SIGUSR1, SIGUSR2 */
33 #include <stdlib.h> /* free() */
34 #include <string.h> /* strerror() */
35 #include <fcntl.h> /* open() */
36 #include <unistd.h> /* close() */
38 #include <termios.h> /* struct termios */
39 #include <sys/ioctl.h>
40 #include <sys/mman.h> /* mmap() */
43 #include <linux/vt.h> /* VT_* */
44 #include <linux/kd.h> /* KD* */
54 #include "video_output.h"
59 /*****************************************************************************
60 * vout_sys_t: video output framebuffer method descriptor
61 *****************************************************************************
62 * This structure is part of the video output thread descriptor.
63 * It describes the FB specific properties of an output thread.
64 *****************************************************************************/
65 typedef struct vout_sys_s
67 /* System informations */
68 int i_tty_dev; /* tty device handle */
69 struct termios old_termios;
71 /* Original configuration informations */
72 struct sigaction sig_usr1; /* USR1 previous handler */
73 struct sigaction sig_usr2; /* USR2 previous handler */
74 struct vt_mode vt_mode; /* previous VT mode */
76 /* Framebuffer information */
77 int i_fb_dev; /* device handle */
78 struct fb_var_screeninfo old_info; /* original mode informations */
79 struct fb_var_screeninfo var_info; /* current mode informations */
80 boolean_t b_pan; /* does device supports panning ? */
81 struct fb_cmap fb_cmap; /* original colormap */
82 u16 *fb_palette; /* original palette */
85 byte_t * p_video; /* base adress */
86 size_t i_page_size; /* page size */
90 /*****************************************************************************
92 *****************************************************************************/
93 static int vout_Probe ( probedata_t *p_data );
94 static int vout_Create ( struct vout_thread_s * );
95 static int vout_Init ( struct vout_thread_s * );
96 static void vout_End ( struct vout_thread_s * );
97 static void vout_Destroy ( struct vout_thread_s * );
98 static int vout_Manage ( struct vout_thread_s * );
99 static void vout_Display ( struct vout_thread_s * );
100 static void vout_SetPalette( p_vout_thread_t p_vout, u16 *red, u16 *green,
101 u16 *blue, u16 *transp );
103 static int FBOpenDisplay ( struct vout_thread_s * );
104 static void FBCloseDisplay ( struct vout_thread_s * );
105 static void FBSwitchDisplay( int i_signal );
106 static void FBTextMode ( int i_tty_dev );
107 static void FBGfxMode ( int i_tty_dev );
109 /*****************************************************************************
110 * Functions exported as capabilities. They are declared as static so that
111 * we don't pollute the namespace too much.
112 *****************************************************************************/
113 void _M( vout_getfunctions )( function_list_t * p_function_list )
115 p_function_list->pf_probe = vout_Probe;
116 p_function_list->functions.vout.pf_create = vout_Create;
117 p_function_list->functions.vout.pf_init = vout_Init;
118 p_function_list->functions.vout.pf_end = vout_End;
119 p_function_list->functions.vout.pf_destroy = vout_Destroy;
120 p_function_list->functions.vout.pf_manage = vout_Manage;
121 p_function_list->functions.vout.pf_display = vout_Display;
122 p_function_list->functions.vout.pf_setpalette = vout_SetPalette;
125 /*****************************************************************************
126 * vout_Probe: probe the video driver and return a score
127 *****************************************************************************
128 * This function tries to open the framebuffer and returns a score to the
129 * plugin manager so that it can select the best plugin.
130 *****************************************************************************/
131 static int vout_Probe( probedata_t *p_data )
135 if( TestMethod( VOUT_METHOD_VAR, "fb" ) )
140 i_fd = open( main_GetPszVariable( VOUT_FB_DEV_VAR,
141 VOUT_FB_DEV_DEFAULT ), O_RDWR );
151 /*****************************************************************************
152 * vout_Create: allocates FB video thread output method
153 *****************************************************************************
154 * This function allocates and initializes a FB vout method.
155 *****************************************************************************/
156 static int vout_Create( vout_thread_t *p_vout )
158 struct sigaction sig_tty; /* sigaction for tty change */
159 struct vt_mode vt_mode; /* vt current mode */
160 struct termios new_termios;
162 /* Allocate instance and initialize some members */
163 p_vout->p_sys = malloc( sizeof( vout_sys_t ) );
164 if( p_vout->p_sys == NULL )
169 /* Set tty and fb devices */
170 p_vout->p_sys->i_tty_dev = 0; /* 0 == /dev/tty0 == current console */
172 FBGfxMode( p_vout->p_sys->i_tty_dev );
174 /* Set keyboard settings */
175 if (tcgetattr(0, &p_vout->p_sys->old_termios) == -1)
177 intf_ErrMsg( "intf error: tcgetattr" );
180 if (tcgetattr(0, &new_termios) == -1)
182 intf_ErrMsg( "intf error: tcgetattr" );
185 /* new_termios.c_lflag &= ~ (ICANON | ISIG);
186 new_termios.c_lflag |= (ECHO | ECHOCTL); */
187 new_termios.c_lflag &= ~ (ICANON);
188 new_termios.c_lflag &= ~(ECHO | ECHOCTL);
189 new_termios.c_iflag = 0;
190 new_termios.c_cc[VMIN] = 1;
191 new_termios.c_cc[VTIME] = 0;
193 if (tcsetattr(0, TCSAFLUSH, &new_termios) == -1)
195 intf_ErrMsg( "intf error: tcsetattr" );
198 ioctl(p_vout->p_sys->i_tty_dev, VT_RELDISP, VT_ACKACQ);
200 /* Set-up tty signal handler to be aware of tty changes */
201 memset( &sig_tty, 0, sizeof( sig_tty ) );
202 sig_tty.sa_handler = FBSwitchDisplay;
203 sigemptyset( &sig_tty.sa_mask );
204 if( sigaction( SIGUSR1, &sig_tty, &p_vout->p_sys->sig_usr1 ) ||
205 sigaction( SIGUSR2, &sig_tty, &p_vout->p_sys->sig_usr2 ) )
207 intf_ErrMsg( "intf error: can't set up signal handler (%s)",
209 tcsetattr(0, 0, &p_vout->p_sys->old_termios);
210 FBTextMode( p_vout->p_sys->i_tty_dev );
211 free( p_vout->p_sys );
215 /* Set-up tty according to new signal handler */
216 if( ioctl(p_vout->p_sys->i_tty_dev, VT_GETMODE, &p_vout->p_sys->vt_mode)
219 intf_ErrMsg( "intf error: cant get terminal mode (%s)",
221 sigaction( SIGUSR1, &p_vout->p_sys->sig_usr1, NULL );
222 sigaction( SIGUSR2, &p_vout->p_sys->sig_usr2, NULL );
223 tcsetattr(0, 0, &p_vout->p_sys->old_termios);
224 FBTextMode( p_vout->p_sys->i_tty_dev );
225 free( p_vout->p_sys );
228 memcpy( &vt_mode, &p_vout->p_sys->vt_mode, sizeof( vt_mode ) );
229 vt_mode.mode = VT_PROCESS;
231 vt_mode.relsig = SIGUSR1;
232 vt_mode.acqsig = SIGUSR2;
234 if( ioctl(p_vout->p_sys->i_tty_dev, VT_SETMODE, &vt_mode) == -1 )
236 intf_ErrMsg( "intf error: can't set terminal mode (%s)",
238 sigaction( SIGUSR1, &p_vout->p_sys->sig_usr1, NULL );
239 sigaction( SIGUSR2, &p_vout->p_sys->sig_usr2, NULL );
240 tcsetattr(0, 0, &p_vout->p_sys->old_termios);
241 FBTextMode( p_vout->p_sys->i_tty_dev );
242 free( p_vout->p_sys );
246 if( FBOpenDisplay( p_vout ) )
248 ioctl(p_vout->p_sys->i_tty_dev, VT_SETMODE, &p_vout->p_sys->vt_mode);
249 sigaction( SIGUSR1, &p_vout->p_sys->sig_usr1, NULL );
250 sigaction( SIGUSR2, &p_vout->p_sys->sig_usr2, NULL );
251 tcsetattr(0, 0, &p_vout->p_sys->old_termios);
252 FBTextMode( p_vout->p_sys->i_tty_dev );
253 free( p_vout->p_sys );
260 /*****************************************************************************
261 * vout_Init: initialize framebuffer video thread output method
262 *****************************************************************************/
263 static int vout_Init( vout_thread_t *p_vout )
265 /* Clear the screen */
266 memset( p_vout->p_sys->p_video, 0, p_vout->p_sys->i_page_size * 2 );
271 /*****************************************************************************
272 * vout_End: terminate framebuffer video thread output method
273 *****************************************************************************/
274 static void vout_End( vout_thread_t *p_vout )
276 /* Clear the screen */
277 memset( p_vout->p_sys->p_video, 0, p_vout->p_sys->i_page_size * 2 );
280 /*****************************************************************************
281 * vout_Destroy: destroy FB video thread output method
282 *****************************************************************************
283 * Terminate an output method created by vout_CreateOutputMethod
284 *****************************************************************************/
285 static void vout_Destroy( vout_thread_t *p_vout )
287 FBCloseDisplay( p_vout );
289 /* Reset the terminal */
290 ioctl(p_vout->p_sys->i_tty_dev, VT_SETMODE, &p_vout->p_sys->vt_mode);
292 /* Remove signal handlers */
293 sigaction( SIGUSR1, &p_vout->p_sys->sig_usr1, NULL );
294 sigaction( SIGUSR2, &p_vout->p_sys->sig_usr2, NULL );
296 /* Reset the keyboard state */
297 tcsetattr( 0, 0, &p_vout->p_sys->old_termios );
299 /* Return to text mode */
300 FBTextMode( p_vout->p_sys->i_tty_dev );
302 /* Destroy structure */
303 free( p_vout->p_sys );
306 /*****************************************************************************
307 * vout_Manage: handle FB events
308 *****************************************************************************
309 * This function should be called regularly by video output thread. It manages
310 * console events. It returns a non null value on error.
311 *****************************************************************************/
312 static int vout_Manage( vout_thread_t *p_vout )
317 if ( read(0, &buf, 1) == 1)
322 p_main->p_intf->b_die = 1;
334 if( p_vout->i_changes & VOUT_SIZE_CHANGE )
336 intf_WarnMsg( 1, "vout: reinitializing framebuffer screen" );
337 p_vout->i_changes &= ~VOUT_SIZE_CHANGE;
339 /* Destroy XImages to change their size */
342 /* Recreate XImages. If SysInit failed, the thread can't go on. */
343 if( vout_Init( p_vout ) )
345 intf_ErrMsg("error: cannot reinit framebuffer screen" );
350 memset( p_vout->p_sys->p_video, 0, p_vout->p_sys->i_page_size * 2 );
353 /* Tell the video output thread that it will need to rebuild YUV
354 * tables. This is needed since conversion buffer size may have changed */
355 p_vout->i_changes |= VOUT_YUV_CHANGE;
362 /*****************************************************************************
363 * vout_Display: displays previously rendered output
364 *****************************************************************************
365 * This function send the currently rendered image to FB image, waits until
366 * it is displayed and switch the two rendering buffers, preparing next frame.
367 *****************************************************************************/
368 static void vout_Display( vout_thread_t *p_vout )
370 /* swap the two Y offsets if the drivers supports panning */
371 if( p_vout->p_sys->b_pan )
373 p_vout->p_sys->var_info.yoffset =
374 p_vout->i_buffer_index ? p_vout->p_sys->var_info.yres : 0;
376 /* the X offset should be 0, but who knows ...
377 * some other app might have played with the framebuffer */
378 p_vout->p_sys->var_info.xoffset = 0;
380 ioctl( p_vout->p_sys->i_fb_dev,
381 FBIOPAN_DISPLAY, &p_vout->p_sys->var_info );
385 /*****************************************************************************
386 * vout_SetPalette: sets an 8 bpp palette
387 *****************************************************************************
388 * This function sets the palette given as an argument. It does not return
389 * anything, but could later send information on which colors it was unable
391 *****************************************************************************/
392 static void vout_SetPalette( p_vout_thread_t p_vout,
393 u16 *red, u16 *green, u16 *blue, u16 *transp )
395 struct fb_cmap cmap = { 0, 256, red, green, blue, transp };
397 ioctl( p_vout->p_sys->i_fb_dev, FBIOPUTCMAP, &cmap );
400 /* following functions are local */
402 /*****************************************************************************
403 * FBOpenDisplay: initialize framebuffer
404 *****************************************************************************/
405 static int FBOpenDisplay( vout_thread_t *p_vout )
407 char *psz_device; /* framebuffer device path */
408 struct fb_fix_screeninfo fix_info; /* framebuffer fix information */
410 /* Open framebuffer device */
411 psz_device = main_GetPszVariable( VOUT_FB_DEV_VAR, VOUT_FB_DEV_DEFAULT );
412 p_vout->p_sys->i_fb_dev = open( psz_device, O_RDWR);
413 if( p_vout->p_sys->i_fb_dev == -1 )
415 intf_ErrMsg("vout error: can't open %s (%s)", psz_device, strerror(errno) );
419 /* Get framebuffer device informations */
420 if( ioctl( p_vout->p_sys->i_fb_dev, FBIOGET_VSCREENINFO, &p_vout->p_sys->var_info ) )
422 intf_ErrMsg("vout error: can't get fb info (%s)", strerror(errno) );
423 close( p_vout->p_sys->i_fb_dev );
427 if( ioctl( p_vout->p_sys->i_fb_dev, FBIOGET_VSCREENINFO, &p_vout->p_sys->old_info ) )
429 intf_ErrMsg("vout error: can't get 2nd fb info (%s)", strerror(errno) );
430 close( p_vout->p_sys->i_fb_dev );
434 /* Set some attributes */
435 p_vout->p_sys->var_info.activate = FB_ACTIVATE_NXTOPEN;
436 p_vout->p_sys->var_info.xoffset = 0;
437 p_vout->p_sys->var_info.yoffset = 0;
439 if( ioctl( p_vout->p_sys->i_fb_dev, FBIOPUT_VSCREENINFO, &p_vout->p_sys->var_info ) )
441 intf_ErrMsg(" vout error: can't set fb info (%s)", strerror(errno) );
442 close( p_vout->p_sys->i_fb_dev );
446 /* Get some informations again, in the definitive configuration */
447 if( ioctl( p_vout->p_sys->i_fb_dev, FBIOGET_FSCREENINFO, &fix_info ) ||
448 ioctl( p_vout->p_sys->i_fb_dev, FBIOGET_VSCREENINFO, &p_vout->p_sys->var_info ) )
450 intf_ErrMsg(" vout error: can't get additional fb info (%s)", strerror(errno) );
452 /* Restore fb config */
453 ioctl( p_vout->p_sys->i_fb_dev, FBIOPUT_VSCREENINFO, &p_vout->p_sys->var_info );
455 close( p_vout->p_sys->i_fb_dev );
459 /* FIXME: if the image is full-size, it gets cropped on the left
460 * because of the xres / xres_virtual slight difference */
461 intf_WarnMsg( 1, "vout: %ix%i (virtual %ix%i)",
462 p_vout->p_sys->var_info.xres,
463 p_vout->p_sys->var_info.yres,
464 p_vout->p_sys->var_info.xres_virtual,
465 p_vout->p_sys->var_info.yres_virtual );
467 p_vout->i_height = p_vout->p_sys->var_info.yres;
468 p_vout->i_width = p_vout->p_sys->var_info.xres_virtual ?
469 p_vout->p_sys->var_info.xres_virtual
470 : p_vout->p_sys->var_info.xres;
472 p_vout->i_screen_depth = p_vout->p_sys->var_info.bits_per_pixel;
474 p_vout->p_sys->fb_palette = NULL;
475 p_vout->p_sys->b_pan = ( fix_info.ypanstep || fix_info.ywrapstep );
477 switch( p_vout->i_screen_depth )
480 p_vout->p_sys->fb_palette = malloc( 8 * 256 * sizeof( u16 ) );
481 p_vout->p_sys->fb_cmap.start = 0;
482 p_vout->p_sys->fb_cmap.len = 256;
483 p_vout->p_sys->fb_cmap.red = p_vout->p_sys->fb_palette;
484 p_vout->p_sys->fb_cmap.green = p_vout->p_sys->fb_palette + 256 * sizeof( u16 );
485 p_vout->p_sys->fb_cmap.blue = p_vout->p_sys->fb_palette + 2 * 256 * sizeof( u16 );
486 p_vout->p_sys->fb_cmap.transp = p_vout->p_sys->fb_palette + 3 * 256 * sizeof( u16 );
488 /* Save the colormap */
489 ioctl( p_vout->p_sys->i_fb_dev, FBIOGETCMAP, &p_vout->p_sys->fb_cmap );
491 p_vout->i_bytes_per_pixel = 1;
492 p_vout->i_bytes_per_line = p_vout->i_width;
495 case 15: /* 15 bpp (16bpp with a missing green bit) */
496 case 16: /* 16 bpp (65536 colors) */
497 p_vout->i_bytes_per_pixel = 2;
498 p_vout->i_bytes_per_line = p_vout->i_width * 2;
501 case 24: /* 24 bpp (millions of colors) */
502 p_vout->i_bytes_per_pixel = 3;
503 p_vout->i_bytes_per_line = p_vout->i_width * 3;
506 case 32: /* 32 bpp (millions of colors) */
507 p_vout->i_bytes_per_pixel = 4;
508 p_vout->i_bytes_per_line = p_vout->i_width * 4;
511 default: /* unsupported screen depth */
512 intf_ErrMsg( "vout error: screen depth %d is not supported",
513 p_vout->i_screen_depth);
515 /* Restore fb config */
516 ioctl( p_vout->p_sys->i_fb_dev, FBIOPUT_VSCREENINFO, &p_vout->p_sys->var_info );
518 close( p_vout->p_sys->i_fb_dev );
523 switch( p_vout->i_screen_depth )
529 p_vout->i_red_mask = ( (1 << p_vout->p_sys->var_info.red.length) - 1 )
530 << p_vout->p_sys->var_info.red.offset;
531 p_vout->i_green_mask = ( (1 << p_vout->p_sys->var_info.green.length) - 1 )
532 << p_vout->p_sys->var_info.green.offset;
533 p_vout->i_blue_mask = ( (1 << p_vout->p_sys->var_info.blue.length) - 1 )
534 << p_vout->p_sys->var_info.blue.offset;
537 p_vout->p_sys->i_page_size = p_vout->i_width *
538 p_vout->i_height * p_vout->i_bytes_per_pixel;
540 /* Map two framebuffers a the very beginning of the fb */
541 p_vout->p_sys->p_video = mmap( 0, p_vout->p_sys->i_page_size * 2,
542 PROT_READ | PROT_WRITE, MAP_SHARED,
543 p_vout->p_sys->i_fb_dev, 0 );
545 if( (int)p_vout->p_sys->p_video == -1 ) /* according to man, it is -1.
548 intf_ErrMsg("vout error: can't map video memory (%s)", strerror(errno) );
549 /* FIXME: restore fb config ?? */
550 if( p_vout->i_screen_depth == 8 )
552 free( p_vout->p_sys->fb_palette );
555 /* Restore fb config */
556 ioctl( p_vout->p_sys->i_fb_dev, FBIOPUT_VSCREENINFO, &p_vout->p_sys->var_info );
558 close( p_vout->p_sys->i_fb_dev );
562 /* Set and initialize buffers */
563 if( p_vout->p_sys->b_pan )
565 vout_SetBuffers( p_vout, p_vout->p_sys->p_video,
566 p_vout->p_sys->p_video
567 + p_vout->p_sys->i_page_size );
571 vout_SetBuffers( p_vout, p_vout->p_sys->p_video,
572 p_vout->p_sys->p_video );
575 intf_WarnMsg( 1, "framebuffer type=%d, visual=%d, ypanstep=%d, ywrap=%d, accel=%d",
576 fix_info.type, fix_info.visual, fix_info.ypanstep, fix_info.ywrapstep, fix_info.accel );
580 /*****************************************************************************
581 * FBCloseDisplay: terminate FB video thread output method
582 *****************************************************************************/
583 static void FBCloseDisplay( vout_thread_t *p_vout )
586 memset( p_vout->p_sys->p_video, 0, p_vout->p_sys->i_page_size * 2 );
588 /* Restore palette */
589 if( p_vout->i_screen_depth == 8 );
591 ioctl( p_vout->p_sys->i_fb_dev, FBIOPUTCMAP, &p_vout->p_sys->fb_cmap );
592 free( p_vout->p_sys->fb_palette );
595 /* Restore fb config */
596 ioctl( p_vout->p_sys->i_fb_dev, FBIOPUT_VSCREENINFO, &p_vout->p_sys->var_info );
599 close( p_vout->p_sys->i_fb_dev );
602 /*****************************************************************************
603 * FBSwitchDisplay: VT change signal handler
604 *****************************************************************************
605 * This function activates or deactivates the output of the thread. It is
606 * called by the VT driver, on terminal change.
607 *****************************************************************************/
608 static void FBSwitchDisplay(int i_signal)
610 if( p_main->p_vout != NULL )
614 case SIGUSR1: /* vt has been released */
615 p_main->p_vout->b_active = 0;
616 ioctl( ((vout_sys_t *)p_main->p_vout->p_sys)->i_tty_dev,
619 case SIGUSR2: /* vt has been acquired */
620 p_main->p_vout->b_active = 1;
621 ioctl( ((vout_sys_t *)p_main->p_vout->p_sys)->i_tty_dev,
622 VT_RELDISP, VT_ACTIVATE );
623 /* handle blanking */
624 vlc_mutex_lock( &p_main->p_vout->change_lock );
625 p_main->p_vout->i_changes |= VOUT_SIZE_CHANGE;
626 vlc_mutex_unlock( &p_main->p_vout->change_lock );
632 /*****************************************************************************
633 * FBTextMode and FBGfxMode : switch tty to text/graphic mode
634 *****************************************************************************
635 * These functions toggle the tty mode.
636 *****************************************************************************/
637 static void FBTextMode( int i_tty_dev )
639 /* return to text mode */
640 if (-1 == ioctl(i_tty_dev, KDSETMODE, KD_TEXT))
642 intf_ErrMsg( "intf error: failed ioctl KDSETMODE KD_TEXT" );
646 static void FBGfxMode( int i_tty_dev )
648 /* switch to graphic mode */
649 if (-1 == ioctl(i_tty_dev, KDSETMODE, KD_GRAPHICS))
651 intf_ErrMsg( "intf error: failed ioctl KDSETMODE KD_GRAPHICS" );