]> git.sesse.net Git - vlc/blob - plugins/fb/vout_fb.c
. fixed the bug-that-made-the-vlc-segfault-on-exit, which means that
[vlc] / plugins / fb / vout_fb.c
1 /*****************************************************************************
2  * vout_fb.c: Linux framebuffer video output display method
3  *****************************************************************************
4  * Copyright (C) 1999, 2000 VideoLAN
5  *
6  * Authors:
7  *
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.
12  * 
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.
17  *
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  *****************************************************************************/
22
23 /*****************************************************************************
24  * Preamble
25  *****************************************************************************/
26 #include "defs.h"
27
28 #include <errno.h>                                                 /* ENOMEM */
29 #include <stdlib.h>                                                /* free() */
30 #include <string.h>                                            /* strerror() */
31
32 #include <fcntl.h>                                                 /* open() */
33 #include <unistd.h>                                               /* close() */
34 #include <linux/fb.h>
35 #include <sys/ioctl.h>
36 #include <sys/mman.h>                                              /* mmap() */
37
38 #include "config.h"
39 #include "common.h"
40 #include "threads.h"
41 #include "mtime.h"
42 #include "plugins.h"
43
44 #include "video.h"
45 #include "video_output.h"
46
47 #include "intf_msg.h"
48 #include "main.h"
49
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
57 {
58     /* System informations */
59     int                         i_fb_dev;       /* framebuffer device handle */
60     struct fb_var_screeninfo    var_info;   /* framebuffer mode informations */
61
62     /* Video memory */
63     byte_t *                    p_video;                      /* base adress */
64     size_t                      i_page_size;                    /* page size */
65
66     struct fb_cmap              fb_cmap;                /* original colormap */
67     unsigned short              *fb_palette;             /* original palette */
68
69 } vout_sys_t;
70
71 /*****************************************************************************
72  * Local prototypes
73  *****************************************************************************/
74 static int     FBOpenDisplay   ( vout_thread_t *p_vout );
75 static void    FBCloseDisplay  ( vout_thread_t *p_vout );
76
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 )
84 {
85     /* Allocate structure */
86     p_vout->p_sys = malloc( sizeof( vout_sys_t ) );
87     if( p_vout->p_sys == NULL )
88     {
89         intf_ErrMsg("error: %s", strerror(ENOMEM) );
90         return( 1 );
91     }
92
93     /* Open and initialize device */
94     if( FBOpenDisplay( p_vout ) )
95     {
96         intf_ErrMsg("vout error: can't open display");
97         free( p_vout->p_sys );
98         return( 1 );
99     }
100
101     return( 0 );
102 }
103
104 /*****************************************************************************
105  * vout_FBInit: initialize framebuffer video thread output method
106  *****************************************************************************/
107 int vout_FBInit( vout_thread_t *p_vout )
108 {
109     return( 0 );
110 }
111
112 /*****************************************************************************
113  * vout_FBEnd: terminate FB video thread output method
114  *****************************************************************************/
115 void vout_FBEnd( vout_thread_t *p_vout )
116 {
117     ;
118 }
119
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 )
126 {
127     FBCloseDisplay( p_vout );
128     free( p_vout->p_sys );
129 }
130
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 )
138 {
139     /*
140      * Size change
141      */
142     if( p_vout->i_changes & VOUT_SIZE_CHANGE )
143     {
144         intf_DbgMsg("resizing window");
145         p_vout->i_changes &= ~VOUT_SIZE_CHANGE;
146
147         /* Destroy XImages to change their size */
148         vout_FBEnd( p_vout );
149
150         /* Recreate XImages. If SysInit failed, the thread can't go on. */
151         if( vout_FBInit( p_vout ) )
152         {
153             intf_ErrMsg("error: can't resize display");
154             return( 1 );
155         }
156
157 #if 1
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("vout: video display resized (%dx%d)", p_vout->i_width, p_vout->i_height);
162 #endif
163     }
164
165     return 0;
166 }
167
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 )
175 {
176     /* swap the two Y offsets */
177     
178 #ifdef FB_NOYPAN
179     
180     /* if the fb doesn't supports ypan, we only use one buffer */
181
182 #else
183     p_vout->p_sys->var_info.yoffset = p_vout->i_buffer_index ? p_vout->p_sys->var_info.yres : 0;
184    
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;
188
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 );
191 #endif
192 }
193
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
199  * to set.
200  *****************************************************************************/
201 void vout_FBSetPalette( p_vout_thread_t p_vout,
202                       u16 *red, u16 *green, u16 *blue, u16 *transp )
203 {
204     struct fb_cmap cmap = { 0, 256, red, green, blue, transp };
205     ioctl( p_vout->p_sys->i_fb_dev, FBIOPUTCMAP, &cmap );
206 }
207
208 /* following functions are local */
209
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
216  * own risk !
217  *****************************************************************************/
218
219 static int FBOpenDisplay( vout_thread_t *p_vout )
220 {
221     char *psz_device;                             /* framebuffer device path */
222     struct fb_fix_screeninfo    fix_info;     /* framebuffer fix information */
223
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 )
228     {
229         intf_ErrMsg("vout error: can't open %s (%s)", psz_device, strerror(errno) );
230         return( 1 );
231     }
232
233     /* Get framebuffer device informations */
234     if( ioctl( p_vout->p_sys->i_fb_dev, FBIOGET_VSCREENINFO, &p_vout->p_sys->var_info ) )
235     {
236         intf_ErrMsg( "vout error: can't get framebuffer informations (%s)", strerror(errno) );
237         close( p_vout->p_sys->i_fb_dev );
238         return( 1 );
239     }
240
241     /* Framebuffer must have some basic properties to be usable */
242     /* XXX?? */
243
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", fix_info.ypanstep );
249     /* XXX?? ask sam p_vout->p_sys->mode_info.sync = FB_SYNC_VERT_HIGH_ACT; */
250
251     if( ioctl( p_vout->p_sys->i_fb_dev, FBIOPUT_VSCREENINFO, &p_vout->p_sys->var_info ) )
252     {
253         intf_ErrMsg("vout error: can't set framebuffer informations (%s)", strerror(errno) );
254         close( p_vout->p_sys->i_fb_dev );
255         return( 1 );
256     }
257
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 ) )
261     {
262         intf_ErrMsg("vout error: can't get framebuffer informations (%s)", strerror(errno) );
263         /* FIXME: restore fb config ?? */
264         close( p_vout->p_sys->i_fb_dev );
265         return( 1 );
266     }
267
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( "vout: %ix%i (virtual %ix%i)", 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 )
275     {
276     case 8:                                                         /* 8 bpp */
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);
284
285         /* saves the colormap */
286         ioctl( p_vout->p_sys->i_fb_dev, FBIOGETCMAP, &p_vout->p_sys->fb_cmap );
287
288         p_vout->i_bytes_per_pixel = 1;
289         p_vout->i_bytes_per_line = p_vout->i_width;
290         break;
291
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;
296         break;
297
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;
301         break;
302
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;
306         break;
307
308     default:                                     /* unsupported screen depth */
309         intf_ErrMsg( "vout error: screen depth %d is not supported",
310                      p_vout->i_screen_depth);
311         return( 1 );
312         break;
313     }
314
315     switch( p_vout->i_screen_depth )
316     {
317     case 15:
318     case 16:
319     case 24:
320     case 32:
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;
327     }
328
329     p_vout->p_sys->i_page_size = p_vout->i_width *
330                 p_vout->i_height * p_vout->i_bytes_per_pixel;
331
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 ? */
338     {
339         intf_ErrMsg("vout error: can't map video memory (%s)", strerror(errno) );
340         /* FIXME: restore fb config ?? */
341         close( p_vout->p_sys->i_fb_dev );
342         return( 1 );
343     }
344
345     /* Set and initialize buffers */
346
347     vout_SetBuffers( p_vout, p_vout->p_sys->p_video,
348
349 #ifdef FB_NOYPAN /* If the fb doesn't support ypan */
350             p_vout->p_sys->p_video 
351 #else
352             p_vout->p_sys->p_video + p_vout->p_sys->i_page_size 
353 #endif
354                 );
355     
356     intf_DbgMsg("framebuffer type=%d, visual=%d, ypanstep=%d, ywrap=%d, accel=%d",
357                 fix_info.type, fix_info.visual, fix_info.ypanstep, fix_info.ywrapstep, fix_info.accel );
358     return( 0 );
359 }
360
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 )
368 {
369     /* Clear display */
370     memset( p_vout->p_sys->p_video, 0, p_vout->p_sys->i_page_size * 2 );
371
372     /* Restore palette */
373     if( p_vout->i_screen_depth == 8 );
374     {
375         ioctl( p_vout->p_sys->i_fb_dev, FBIOPUTCMAP, &p_vout->p_sys->fb_cmap );
376         free( p_vout->p_sys->fb_palette );
377     }
378
379     /* Destroy window and close display */
380     close( p_vout->p_sys->i_fb_dev );
381 }
382
383