1 /*****************************************************************************
2 * vout_fb.c: Linux framebuffer video output display method
3 *****************************************************************************
4 * Copyright (C) 1999, 2000 VideoLAN
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 /*****************************************************************************
25 *****************************************************************************/
28 #include <errno.h> /* ENOMEM */
29 #include <stdlib.h> /* free() */
30 #include <string.h> /* strerror() */
32 #include <fcntl.h> /* open() */
33 #include <unistd.h> /* close() */
35 #include <sys/ioctl.h>
36 #include <sys/mman.h> /* mmap() */
45 #include "video_output.h"
50 /*****************************************************************************
51 * vout_sys_t: video output framebuffer method descriptor
52 *****************************************************************************
53 * This structure is part of the video output thread descriptor.
54 * It describes the FB specific properties of an output thread.
55 *****************************************************************************/
56 typedef struct vout_sys_s
58 /* System informations */
59 int i_fb_dev; /* framebuffer device handle */
60 struct fb_var_screeninfo var_info; /* framebuffer mode informations */
63 byte_t * p_video; /* base adress */
64 size_t i_page_size; /* page size */
66 struct fb_cmap fb_cmap; /* original colormap */
67 unsigned short *fb_palette; /* original palette */
71 /*****************************************************************************
73 *****************************************************************************/
74 static int FBOpenDisplay ( vout_thread_t *p_vout );
75 static void FBCloseDisplay ( vout_thread_t *p_vout );
77 /*****************************************************************************
78 * vout_FBCreate: allocates FB video thread output method
79 *****************************************************************************
80 * This function allocates and initializes a FB vout method.
81 *****************************************************************************/
82 int vout_FBCreate( vout_thread_t *p_vout, char *psz_display,
83 int i_root_window, void *p_data )
85 /* Allocate structure */
86 p_vout->p_sys = malloc( sizeof( vout_sys_t ) );
87 if( p_vout->p_sys == NULL )
89 intf_ErrMsg("error: %s\n", strerror(ENOMEM) );
93 /* Open and initialize device */
94 if( FBOpenDisplay( p_vout ) )
96 intf_ErrMsg("vout error: can't open display\n");
97 free( p_vout->p_sys );
104 /*****************************************************************************
105 * vout_FBInit: initialize framebuffer video thread output method
106 *****************************************************************************/
107 int vout_FBInit( vout_thread_t *p_vout )
112 /*****************************************************************************
113 * vout_FBEnd: terminate FB video thread output method
114 *****************************************************************************/
115 void vout_FBEnd( vout_thread_t *p_vout )
120 /*****************************************************************************
121 * vout_FBDestroy: destroy FB video thread output method
122 *****************************************************************************
123 * Terminate an output method created by vout_CreateOutputMethod
124 *****************************************************************************/
125 void vout_FBDestroy( vout_thread_t *p_vout )
127 FBCloseDisplay( p_vout );
128 free( p_vout->p_sys );
131 /*****************************************************************************
132 * vout_FBManage: handle FB events
133 *****************************************************************************
134 * This function should be called regularly by video output thread. It manages
135 * console events. It returns a non null value on error.
136 *****************************************************************************/
137 int vout_FBManage( vout_thread_t *p_vout )
142 if( p_vout->i_changes & VOUT_SIZE_CHANGE )
144 intf_DbgMsg("resizing window\n");
145 p_vout->i_changes &= ~VOUT_SIZE_CHANGE;
147 /* Destroy XImages to change their size */
148 vout_FBEnd( p_vout );
150 /* Recreate XImages. If SysInit failed, the thread can't go on. */
151 if( vout_FBInit( p_vout ) )
153 intf_ErrMsg("error: can't resize display\n");
158 /* Tell the video output thread that it will need to rebuild YUV
159 * tables. This is needed since conversion buffer size may have changed */
160 p_vout->i_changes |= VOUT_YUV_CHANGE;
161 intf_Msg("Video display resized (%dx%d)\n", p_vout->i_width, p_vout->i_height);
168 /*****************************************************************************
169 * vout_FBDisplay: displays previously rendered output
170 *****************************************************************************
171 * This function send the currently rendered image to FB image, waits until
172 * it is displayed and switch the two rendering buffers, preparing next frame.
173 *****************************************************************************/
174 void vout_FBDisplay( vout_thread_t *p_vout )
176 /* swap the two Y offsets */
180 /* if the fb doesn't supports ypan, we only use one buffer */
183 p_vout->p_sys->var_info.yoffset = p_vout->i_buffer_index ? p_vout->p_sys->var_info.yres : 0;
185 /* the X offset should be 0, but who knows ...
186 * some other app might have played with the framebuffer */
187 p_vout->p_sys->var_info.xoffset = 0;
189 //ioctl( p_vout->p_sys->i_fb_dev, FBIOPUT_VSCREENINFO, &p_vout->p_sys->var_info );
190 ioctl( p_vout->p_sys->i_fb_dev, FBIOPAN_DISPLAY, &p_vout->p_sys->var_info );
194 /*****************************************************************************
195 * vout_FBSetPalette: sets an 8 bpp palette
196 *****************************************************************************
197 * This function sets the palette given as an argument. It does not return
198 * anything, but could later send information on which colors it was unable
200 *****************************************************************************/
201 void vout_FBSetPalette( p_vout_thread_t p_vout,
202 u16 *red, u16 *green, u16 *blue, u16 *transp )
204 struct fb_cmap cmap = { 0, 256, red, green, blue, transp };
205 ioctl( p_vout->p_sys->i_fb_dev, FBIOPUTCMAP, &cmap );
208 /* following functions are local */
210 /*****************************************************************************
211 * FBOpenDisplay: open and initialize framebuffer device
212 *****************************************************************************
213 * XXX?? The framebuffer mode is only provided as a fast and efficient way to
214 * display video, providing the card is configured and the mode ok. It is
215 * not portable, and is not supposed to work with many cards. Use at your
217 *****************************************************************************/
219 static int FBOpenDisplay( vout_thread_t *p_vout )
221 char *psz_device; /* framebuffer device path */
222 struct fb_fix_screeninfo fix_info; /* framebuffer fix information */
224 /* Open framebuffer device */
225 psz_device = main_GetPszVariable( VOUT_FB_DEV_VAR, VOUT_FB_DEV_DEFAULT );
226 p_vout->p_sys->i_fb_dev = open( psz_device, O_RDWR);
227 if( p_vout->p_sys->i_fb_dev == -1 )
229 intf_ErrMsg("vout error: can't open %s (%s)\n", psz_device, strerror(errno) );
233 /* Get framebuffer device informations */
234 if( ioctl( p_vout->p_sys->i_fb_dev, FBIOGET_VSCREENINFO, &p_vout->p_sys->var_info ) )
236 intf_ErrMsg( "vout error: can't get framebuffer informations (%s)\n", strerror(errno) );
237 close( p_vout->p_sys->i_fb_dev );
241 /* Framebuffer must have some basic properties to be usable */
244 /* Set some attributes */
245 p_vout->p_sys->var_info.activate = FB_ACTIVATE_NXTOPEN;
246 p_vout->p_sys->var_info.xoffset = 0;
247 p_vout->p_sys->var_info.yoffset = 0;
248 intf_ErrMsg( "vout: ypanstep is %i\n", fix_info.ypanstep );
249 /* XXX?? ask sam p_vout->p_sys->mode_info.sync = FB_SYNC_VERT_HIGH_ACT; */
251 if( ioctl( p_vout->p_sys->i_fb_dev, FBIOPUT_VSCREENINFO, &p_vout->p_sys->var_info ) )
253 intf_ErrMsg("vout error: can't set framebuffer informations (%s)\n", strerror(errno) );
254 close( p_vout->p_sys->i_fb_dev );
258 /* Get some informations again, in the definitive configuration */
259 if( ioctl( p_vout->p_sys->i_fb_dev, FBIOGET_FSCREENINFO, &fix_info ) ||
260 ioctl( p_vout->p_sys->i_fb_dev, FBIOGET_VSCREENINFO, &p_vout->p_sys->var_info ) )
262 intf_ErrMsg("vout error: can't get framebuffer informations (%s)\n", strerror(errno) );
263 /* FIXME: restore fb config ?? */
264 close( p_vout->p_sys->i_fb_dev );
268 /* FIXME: if the image is full-size, it gets cropped on the left
269 * because of the xres / xres_virtual slight difference */
270 intf_Msg( "%ix%i (virtual %ix%i)\n", p_vout->p_sys->var_info.xres, p_vout->p_sys->var_info.yres, p_vout->p_sys->var_info.xres_virtual, p_vout->p_sys->var_info.yres_virtual );
271 p_vout->i_width = p_vout->p_sys->var_info.xres_virtual ? p_vout->p_sys->var_info.xres_virtual : p_vout->p_sys->var_info.xres;
272 p_vout->i_height = p_vout->p_sys->var_info.yres;
273 p_vout->i_screen_depth = p_vout->p_sys->var_info.bits_per_pixel;
274 switch( p_vout->i_screen_depth )
277 p_vout->p_sys->fb_palette = malloc( 8 * 256 * sizeof(unsigned short) );
278 p_vout->p_sys->fb_cmap.start = 0;
279 p_vout->p_sys->fb_cmap.len = 256;
280 p_vout->p_sys->fb_cmap.red = p_vout->p_sys->fb_palette;
281 p_vout->p_sys->fb_cmap.green = p_vout->p_sys->fb_palette + 256 * sizeof(unsigned short);
282 p_vout->p_sys->fb_cmap.blue = p_vout->p_sys->fb_palette + 2 * 256 * sizeof(unsigned short);
283 p_vout->p_sys->fb_cmap.transp = p_vout->p_sys->fb_palette + 3 * 256 * sizeof(unsigned short);
285 /* saves the colormap */
286 ioctl( p_vout->p_sys->i_fb_dev, FBIOGETCMAP, &p_vout->p_sys->fb_cmap );
288 p_vout->i_bytes_per_pixel = 1;
289 p_vout->i_bytes_per_line = p_vout->i_width;
292 case 15: /* 15 bpp (16bpp with a missing green bit) */
293 case 16: /* 16 bpp (65536 colors) */
294 p_vout->i_bytes_per_pixel = 2;
295 p_vout->i_bytes_per_line = p_vout->i_width * 2;
298 case 24: /* 24 bpp (millions of colors) */
299 p_vout->i_bytes_per_pixel = 3;
300 p_vout->i_bytes_per_line = p_vout->i_width * 3;
303 case 32: /* 32 bpp (millions of colors) */
304 p_vout->i_bytes_per_pixel = 4;
305 p_vout->i_bytes_per_line = p_vout->i_width * 4;
308 default: /* unsupported screen depth */
309 intf_ErrMsg( "vout error: screen depth %d is not supported\n",
310 p_vout->i_screen_depth);
315 switch( p_vout->i_screen_depth )
321 p_vout->i_red_mask = ( (1 << p_vout->p_sys->var_info.red.length) - 1 )
322 << p_vout->p_sys->var_info.red.offset;
323 p_vout->i_green_mask = ( (1 << p_vout->p_sys->var_info.green.length) - 1 )
324 << p_vout->p_sys->var_info.green.offset;
325 p_vout->i_blue_mask = ( (1 << p_vout->p_sys->var_info.blue.length) - 1 )
326 << p_vout->p_sys->var_info.blue.offset;
329 p_vout->p_sys->i_page_size = p_vout->i_width *
330 p_vout->i_height * p_vout->i_bytes_per_pixel;
332 /* Map two framebuffers a the very beginning of the fb */
333 p_vout->p_sys->p_video = mmap(0, p_vout->p_sys->i_page_size * 2,
334 PROT_READ | PROT_WRITE, MAP_SHARED,
335 p_vout->p_sys->i_fb_dev, 0 );
336 memset( p_vout->p_sys->p_video, 0, p_vout->p_sys->i_page_size * 2 );
337 if( (int)p_vout->p_sys->p_video == -1 ) /* according to man, it is -1. What about NULL ? */
339 intf_ErrMsg("vout error: can't map video memory (%s)\n", strerror(errno) );
340 /* FIXME: restore fb config ?? */
341 close( p_vout->p_sys->i_fb_dev );
345 /* Set and initialize buffers */
347 vout_SetBuffers( p_vout, p_vout->p_sys->p_video,
349 #ifdef FB_NOYPAN /* If the fb doesn't support ypan */
350 p_vout->p_sys->p_video
352 p_vout->p_sys->p_video + p_vout->p_sys->i_page_size
356 intf_DbgMsg("framebuffer type=%d, visual=%d, ypanstep=%d, ywrap=%d, accel=%d\n",
357 fix_info.type, fix_info.visual, fix_info.ypanstep, fix_info.ywrapstep, fix_info.accel );
361 /*****************************************************************************
362 * FBCloseDisplay: close and reset framebuffer device
363 *****************************************************************************
364 * Returns all resources allocated by FBOpenDisplay and restore the original
365 * state of the device.
366 *****************************************************************************/
367 static void FBCloseDisplay( vout_thread_t *p_vout )
370 memset( p_vout->p_sys->p_video, 0, p_vout->p_sys->i_page_size * 2 );
372 /* Restore palette */
373 if( p_vout->i_screen_depth == 8 );
375 ioctl( p_vout->p_sys->i_fb_dev, FBIOPUTCMAP, &p_vout->p_sys->fb_cmap );
376 free( p_vout->p_sys->fb_palette );
379 /* Destroy window and close display */
380 close( p_vout->p_sys->i_fb_dev );