]> git.sesse.net Git - vlc/blob - modules/gui/fbosd.c
macosx: Make sure we don't leak an input.
[vlc] / modules / gui / fbosd.c
1 /*****************************************************************************
2  * fbosd.c : framebuffer osd plugin for vlc
3  *****************************************************************************
4  * Copyright (C) 2007-2008, the VideoLAN team
5  * $Id$
6  *
7  * Authors: Jean-Paul Saman
8  * Copied from modules/video_output/fb.c by Samuel Hocevar <sam@zoy.org>
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 #ifdef HAVE_CONFIG_H
29 # include "config.h"
30 #endif
31
32 #include <vlc_common.h>
33 #include <vlc_plugin.h>
34
35 #include <errno.h>
36 #include <stdlib.h>                                                /* free() */
37 #include <string.h>                                            /* strerror() */
38 #include <fcntl.h>                                                 /* open() */
39 #include <unistd.h>                                               /* close() */
40
41 #include <sys/ioctl.h>
42 #include <sys/mman.h>                                              /* mmap() */
43
44 #include <linux/fb.h>
45
46 #include <vlc_image.h>
47 #include <vlc_interface.h>
48 #include <vlc_input.h>
49 #include <vlc_vout.h>
50 #include <vlc_filter.h>
51 #include <vlc_osd.h>
52 #include <vlc_strings.h>
53
54 #undef FBOSD_BLENDING
55 #undef FBOSD_DEBUG
56
57 /*****************************************************************************
58  * Local prototypes
59  *****************************************************************************/
60 static int  Create    ( vlc_object_t * );
61 static void Destroy   ( vlc_object_t * );
62 static void Run       ( intf_thread_t * );
63
64 static int  Init      ( intf_thread_t * );
65 static void End       ( intf_thread_t * );
66
67 static int  OpenDisplay    ( intf_thread_t * );
68 static void CloseDisplay   ( intf_thread_t * );
69
70 /* Load modules needed for rendering and blending */
71 #if defined(FBOSD_BLENDING)
72 static int  OpenBlending     ( intf_thread_t * );
73 static void CloseBlending    ( intf_thread_t * );
74 #endif
75 static int  OpenTextRenderer ( intf_thread_t * );
76 static void CloseTextRenderer( intf_thread_t * );
77
78 /* Manipulate the overlay buffer */
79 static int  OverlayCallback( vlc_object_t *, char const *,
80                              vlc_value_t, vlc_value_t, void * );
81
82 static picture_t *AllocatePicture( vlc_object_t *,
83                                          video_format_t * );
84 static void DeAllocatePicture( vlc_object_t *, picture_t *,
85                                      video_format_t * );
86 static void SetOverlayTransparency( intf_thread_t *,
87                                     bool );
88 static picture_t *LoadImage( intf_thread_t *, video_format_t *,
89                              char * );
90
91 #if defined(FBOSD_BLENDING)
92 static int BlendPicture( intf_thread_t *, video_format_t *,
93                          video_format_t *, picture_t *, picture_t * );
94 #else
95 static picture_t *ConvertImage( intf_thread_t *, picture_t *,
96                                 video_format_t *, video_format_t * );
97 #endif
98 static int RenderPicture( intf_thread_t *, int, int,
99                           picture_t *, picture_t * );
100 static picture_t *RenderText( intf_thread_t *, const char *,
101                               text_style_t *, video_format_t * );
102
103 #define DEVICE_TEXT N_("Framebuffer device")
104 #define DEVICE_LONGTEXT N_( \
105     "Framebuffer device to use for rendering (usually /dev/fb0).")
106
107 #define ASPECT_RATIO_TEXT N_("Video aspect ratio")
108 #define ASPECT_RATIO_LONGTEXT N_( \
109     "Aspect ratio of the video image (4:3, 16:9). Default is square pixels." )
110
111 #define FBOSD_IMAGE_TEXT N_("Image file")
112 #define FBOSD_IMAGE_LONGTEXT N_( \
113     "Filename of image file to use on the overlay framebuffer." )
114
115 #define ALPHA_TEXT N_("Transparency of the image")
116 #define ALPHA_LONGTEXT N_( "Transparency value of the new image " \
117     "used in blending. By default it set to fully opaque (255). " \
118     "(from 0 for full transparency to 255 for full opacity)" )
119
120 #define FBOSD_TEXT N_("Text")
121 #define FBOSD_LONGTEXT N_( "Text to display on the overlay framebuffer." )
122
123 #define POSX_TEXT N_("X coordinate")
124 #define POSX_LONGTEXT N_("X coordinate of the rendered image")
125
126 #define POSY_TEXT N_("Y coordinate")
127 #define POSY_LONGTEXT N_("Y coordinate of the rendered image")
128
129 #define POS_TEXT N_("Position")
130 #define POS_LONGTEXT N_( \
131   "You can enforce the picture position on the overlay " \
132   "(0=center, 1=left, 2=right, 4=top, 8=bottom, you can " \
133   "also use combinations of these values, e.g. 6=top-right).")
134
135 #define OPACITY_TEXT N_("Opacity")
136 #define OPACITY_LONGTEXT N_("Opacity (inverse of transparency) of " \
137     "overlayed text. 0 = transparent, 255 = totally opaque. " )
138
139 #define SIZE_TEXT N_("Font size, pixels")
140 #define SIZE_LONGTEXT N_("Font size, in pixels. Default is -1 (use default " \
141     "font size)." )
142
143 #define COLOR_TEXT N_("Color")
144 #define COLOR_LONGTEXT N_("Color of the text that will be rendered on "\
145     "the video. This must be an hexadecimal (like HTML colors). The first two "\
146     "chars are for red, then green, then blue. #000000 = black, #FF0000 = red,"\
147     " #00FF00 = green, #FFFF00 = yellow (red + green), #FFFFFF = white" )
148
149 #define CLEAR_TEXT N_( "Clear overlay framebuffer" )
150 #define CLEAR_LONGTEXT N_( "The displayed overlay images is cleared by " \
151     "making the overlay completely transparent. All previously rendered " \
152     "images and text will be cleared from the cache." )
153
154 #define RENDER_TEXT N_( "Render text or image" )
155 #define RENDER_LONGTEXT N_( "Render the image or text in current overlay " \
156     "buffer." )
157
158 #define DISPLAY_TEXT N_( "Display on overlay framebuffer" )
159 #define DISPLAY_LONGTEXT N_( "All rendered images and text will be " \
160     "displayed on the overlay framebuffer." )
161
162 static const int pi_pos_values[] = { 0, 1, 2, 4, 8, 5, 6, 9, 10 };
163 static const char *const ppsz_pos_descriptions[] =
164 { N_("Center"), N_("Left"), N_("Right"), N_("Top"), N_("Bottom"),
165   N_("Top-Left"), N_("Top-Right"), N_("Bottom-Left"), N_("Bottom-Right") };
166
167 static const int pi_color_values[] = {
168                0xf0000000, 0x00000000, 0x00808080, 0x00C0C0C0,
169                0x00FFFFFF, 0x00800000, 0x00FF0000, 0x00FF00FF, 0x00FFFF00,
170                0x00808000, 0x00008000, 0x00008080, 0x0000FF00, 0x00800080,
171                0x00000080, 0x000000FF, 0x0000FFFF};
172 static const char *const ppsz_color_descriptions[] = {
173                N_("Default"), N_("Black"),
174                N_("Gray"), N_("Silver"), N_("White"), N_("Maroon"), N_("Red"),
175                N_("Fuchsia"), N_("Yellow"), N_("Olive"), N_("Green"),
176                N_("Teal"), N_("Lime"), N_("Purple"), N_("Navy"), N_("Blue"),
177                N_("Aqua") };
178
179 vlc_module_begin();
180     set_shortname( "fbosd" );
181     set_category( CAT_INTERFACE );
182     set_subcategory( SUBCAT_INTERFACE_MAIN );
183
184     add_file( "fbosd-dev", "/dev/fb1", NULL, DEVICE_TEXT, DEVICE_LONGTEXT,
185               false );
186     add_string( "fbosd-aspect-ratio", "", NULL, ASPECT_RATIO_TEXT,
187                 ASPECT_RATIO_LONGTEXT, true );
188
189     add_string( "fbosd-image", NULL, NULL, FBOSD_IMAGE_TEXT,
190                 FBOSD_IMAGE_LONGTEXT, true );
191     add_string( "fbosd-text", NULL, NULL, FBOSD_TEXT,
192                 FBOSD_LONGTEXT, true );
193
194     add_integer_with_range( "fbosd-alpha", 255, 0, 255, NULL, ALPHA_TEXT,
195                             ALPHA_LONGTEXT, true );
196
197     set_section( N_("Position"), NULL );
198     add_integer( "fbosd-x", 0, NULL, POSX_TEXT,
199                  POSX_LONGTEXT, false );
200     add_integer( "fbosd-y", 0, NULL, POSY_TEXT,
201                  POSY_LONGTEXT, false );
202     add_integer( "fbosd-position", 8, NULL, POS_TEXT, POS_LONGTEXT, true );
203         change_integer_list( pi_pos_values, ppsz_pos_descriptions, 0 );
204
205     set_section( N_("Font"), NULL );
206     add_integer_with_range( "fbosd-font-opacity", 255, 0, 255, NULL,
207         OPACITY_TEXT, OPACITY_LONGTEXT, false );
208     add_integer( "fbosd-font-color", 0x00FFFFFF, NULL, COLOR_TEXT, COLOR_LONGTEXT,
209                  false );
210         change_integer_list( pi_color_values, ppsz_color_descriptions, 0 );
211     add_integer( "fbosd-font-size", -1, NULL, SIZE_TEXT, SIZE_LONGTEXT,
212                  false );
213
214     set_section( N_("Commands"), NULL );
215     add_bool( "fbosd-clear", false, NULL, CLEAR_TEXT, CLEAR_LONGTEXT, true );
216     add_bool( "fbosd-render", false, NULL, RENDER_TEXT, RENDER_LONGTEXT, true );
217     add_bool( "fbosd-display", false, NULL, DISPLAY_TEXT, DISPLAY_LONGTEXT, true );
218
219     set_description( N_("GNU/Linux osd/overlay framebuffer interface") );
220     set_capability( "interface", 10 );
221     set_callbacks( Create, Destroy );
222 vlc_module_end();
223
224 /*****************************************************************************
225  * fbosd_render_t: render descriptor
226  *****************************************************************************/
227 struct fbosd_render_t
228 {
229 #define FBOSD_RENDER_IMAGE 0
230 #define FBOSD_RENDER_TEXT  1
231     int             i_type;
232
233 #define FBOSD_STATE_FREE     0
234 #define FBOSD_STATE_RESERVED 1
235 #define FBOSD_STATE_RENDER   2
236     int             i_state;
237
238     /* Font style */
239     text_style_t    text_style;                              /* font control */
240     char            *psz_string;
241
242     /* Position */
243     bool            b_absolute;
244     int             i_x;
245     int             i_y;
246     int             i_pos;
247     int             i_alpha;                      /* transparency for images */
248 };
249 #define FBOSD_RENDER_MAX 10
250
251 /*****************************************************************************
252  * intf_sys_t: interface framebuffer method descriptor
253  *****************************************************************************/
254 struct intf_sys_t
255 {
256     /* Framebuffer information */
257     int                         i_fd;                       /* device handle */
258     struct fb_var_screeninfo    var_info;        /* current mode information */
259     bool                  b_pan;     /* does device supports panning ? */
260     struct fb_cmap              fb_cmap;                /* original colormap */
261     uint16_t                    *p_palette;              /* original palette */
262
263     /* Overlay framebuffer format */
264     video_format_t  fmt_out;
265     picture_t       *p_overlay;
266     size_t          i_page_size;                                /* page size */
267     int             i_width;
268     int             i_height;
269     int             i_aspect;
270     int             i_bytes_per_pixel;
271
272     /* Image and Picture rendering */
273     image_handler_t *p_image;
274 #if defined(FBOSD_BLENDING)
275     filter_t *p_blend;                              /* alpha blending module */
276 #endif
277     filter_t *p_text;                                /* text renderer module */
278
279     /* Render */
280     struct fbosd_render_t render[FBOSD_RENDER_MAX];
281
282     /* Font style */
283     text_style_t    *p_style;                                /* font control */
284
285     /* Position */
286     bool      b_absolute;
287     int       i_x;
288     int       i_y;
289     int       i_pos;
290
291     int       i_alpha;                      /* transparency for images */
292
293     /* commands control */
294     bool      b_need_update;    /* update display with \overlay buffer */
295     bool      b_clear;      /* clear overlay buffer make it tranparent */
296     bool      b_render;   /* render an image or text in overlay buffer */
297 };
298
299 /*****************************************************************************
300  * Create: allocates FB interface thread output method
301  *****************************************************************************/
302 static int Create( vlc_object_t *p_this )
303 {
304     intf_thread_t *p_intf = (intf_thread_t *)p_this;
305     intf_sys_t    *p_sys;
306     char          *psz_aspect;
307     char          *psz_tmp;
308     int i;
309
310     /* Allocate instance and initialize some members */
311     p_intf->p_sys = p_sys = malloc( sizeof( intf_sys_t ) );
312     if( !p_intf->p_sys )
313         return VLC_ENOMEM;
314     memset( p_sys, 0, sizeof(intf_sys_t) );
315
316     p_sys->p_style = malloc( sizeof( text_style_t ) );
317     if( !p_sys->p_style )
318     {
319         free( p_intf->p_sys );
320         return VLC_ENOMEM;
321     }
322     vlc_memcpy( p_sys->p_style, &default_text_style, sizeof( text_style_t ) );
323
324     p_intf->pf_run = Run;
325
326     p_sys->p_image = image_HandlerCreate( p_this );
327     if( !p_sys->p_image )
328     {
329         free( p_intf->p_sys->p_style );
330         free( p_intf->p_sys );
331         return VLC_ENOMEM;
332     }
333
334     p_sys->i_alpha = var_CreateGetIntegerCommand( p_intf, "fbosd-alpha" );
335     var_AddCallback( p_intf, "fbosd-alpha", OverlayCallback, NULL );
336
337     p_sys->i_aspect = -1;
338     psz_aspect =
339             var_CreateGetNonEmptyString( p_intf, "fbosd-aspect-ratio" );
340     if( psz_aspect )
341     {
342         char *psz_parser = strchr( psz_aspect, ':' );
343
344         if( psz_parser )
345         {
346             *psz_parser++ = '\0';
347             p_sys->i_aspect = ( atoi( psz_aspect )
348                               * VOUT_ASPECT_FACTOR ) / atoi( psz_parser );
349             p_sys->fmt_out.i_aspect = p_sys->i_aspect;
350         }
351         msg_Dbg( p_intf, "using aspect ratio %d:%d",
352                   atoi( psz_aspect ), atoi( psz_parser ) );
353
354         free( psz_aspect );
355         psz_aspect = NULL;
356     }
357
358     /* Use PAL by default */
359     p_sys->i_width  = p_sys->fmt_out.i_width  = 704;
360     p_sys->i_height = p_sys->fmt_out.i_height = 576;
361
362     psz_tmp = var_CreateGetNonEmptyStringCommand( p_intf, "fbosd-image" );
363     var_AddCallback( p_intf, "fbosd-image", OverlayCallback, NULL );
364     if( psz_tmp && *psz_tmp )
365     {
366         p_sys->render[0].i_type = FBOSD_RENDER_IMAGE;
367         p_sys->render[0].i_state = FBOSD_STATE_RENDER;
368         p_sys->render[0].psz_string = strdup( psz_tmp );
369     }
370     free( psz_tmp );
371
372     psz_tmp = var_CreateGetNonEmptyStringCommand( p_intf, "fbosd-text" );
373     var_AddCallback( p_intf, "fbosd-text", OverlayCallback, NULL );
374     if( psz_tmp && *psz_tmp )
375     {
376         p_sys->render[1].i_type = FBOSD_RENDER_TEXT;
377         p_sys->render[1].i_state = FBOSD_STATE_RENDER;
378         p_sys->render[1].psz_string = strdup( psz_tmp );
379     }
380     free( psz_tmp );
381
382     p_sys->i_pos = var_CreateGetIntegerCommand( p_intf, "fbosd-position" );
383     p_sys->i_x = var_CreateGetIntegerCommand( p_intf, "fbosd-x" );
384     p_sys->i_y = var_CreateGetIntegerCommand( p_intf, "fbosd-y" );
385
386     var_AddCallback( p_intf, "fbosd-position", OverlayCallback, NULL );
387     var_AddCallback( p_intf, "fbosd-x", OverlayCallback, NULL );
388     var_AddCallback( p_intf, "fbosd-y", OverlayCallback, NULL );
389
390     p_sys->p_style->i_font_size =
391             var_CreateGetIntegerCommand( p_intf, "fbosd-font-size" );
392     p_sys->p_style->i_font_color =
393             var_CreateGetIntegerCommand( p_intf, "fbosd-font-color" );
394     p_sys->p_style->i_font_alpha = 255 -
395             var_CreateGetIntegerCommand( p_intf, "fbosd-font-opacity" );
396
397     var_AddCallback( p_intf, "fbosd-font-color", OverlayCallback, NULL );
398     var_AddCallback( p_intf, "fbosd-font-size", OverlayCallback, NULL );
399     var_AddCallback( p_intf, "fbosd-font-opacity", OverlayCallback, NULL );
400
401     for( i = 0; i < FBOSD_RENDER_MAX; i++ )
402     {
403         vlc_memcpy( &p_sys->render[i].text_style, &default_text_style,
404                     sizeof( text_style_t ) );
405     }
406
407     p_sys->b_clear = var_CreateGetBoolCommand( p_intf, "fbosd-clear" );
408     p_sys->b_render = var_CreateGetBoolCommand( p_intf, "fbosd-render" );
409     p_sys->b_need_update = var_CreateGetBoolCommand( p_intf, "fbosd-display" );
410
411     var_AddCallback( p_intf, "fbosd-clear", OverlayCallback, NULL );
412     var_AddCallback( p_intf, "fbosd-render", OverlayCallback, NULL );
413     var_AddCallback( p_intf, "fbosd-display", OverlayCallback, NULL );
414
415     /* Check if picture position was overridden */
416     p_sys->b_absolute = true;
417     if( ( p_sys->i_x >= 0 ) && ( p_sys->i_y >= 0 ) )
418     {
419         p_sys->b_absolute = false;
420         p_sys->i_y = (p_sys->i_y < p_sys->i_height) ?
421                         p_sys->i_y : p_sys->i_height;
422         p_sys->i_x = (p_sys->i_x < p_sys->i_width) ?
423                         p_sys->i_x : p_sys->i_width;
424     }
425
426     p_sys->render[0].i_x = p_sys->render[1].i_x = p_sys->i_x;
427     p_sys->render[0].i_y = p_sys->render[1].i_y = p_sys->i_y;
428     p_sys->render[0].i_pos = p_sys->render[1].i_pos = p_sys->i_pos;
429     p_sys->render[0].i_alpha = p_sys->render[1].i_alpha = p_sys->i_alpha;
430
431     /* Initialize framebuffer */
432     if( OpenDisplay( p_intf ) )
433     {
434         Destroy( VLC_OBJECT(p_intf) );
435         return VLC_EGENERIC;
436     }
437
438     Init( p_intf );
439
440 #if defined(FBOSD_BLENDING)
441     /* Load the blending module */
442     if( OpenBlending( p_intf ) )
443     {
444         msg_Err( p_intf, "Unable to load image blending module" );
445         Destroy( VLC_OBJECT(p_intf) );
446         return VLC_EGENERIC;
447     }
448 #endif
449
450     /* Load text renderer module */
451     if( OpenTextRenderer( p_intf ) )
452     {
453         msg_Err( p_intf, "Unable to load text rendering module" );
454         Destroy( VLC_OBJECT(p_intf) );
455         return VLC_EGENERIC;
456     }
457
458     p_sys->b_render = true;
459     p_sys->b_need_update = true;
460
461     return VLC_SUCCESS;
462 }
463
464 /*****************************************************************************
465  * Destroy: destroy FB interface thread output method
466  *****************************************************************************
467  * Terminate an output method created by Create
468  *****************************************************************************/
469 static void Destroy( vlc_object_t *p_this )
470 {
471     intf_thread_t *p_intf = (intf_thread_t *)p_this;
472     intf_sys_t *p_sys = (intf_sys_t *) p_intf->p_sys;
473     int i;
474
475     p_sys->b_need_update = false;
476     p_sys->b_render = false;
477     p_sys->b_clear = false;
478
479     var_DelCallback( p_intf, "fbosd-alpha", OverlayCallback, NULL );
480     var_Destroy( p_intf, "fbosd-alpha" );
481
482     var_DelCallback( p_intf, "fbosd-x", OverlayCallback, NULL );
483     var_DelCallback( p_intf, "fbosd-y", OverlayCallback, NULL );
484     var_DelCallback( p_intf, "fbosd-position", OverlayCallback, NULL );
485     var_DelCallback( p_intf, "fbosd-image", OverlayCallback, NULL );
486     var_DelCallback( p_intf, "fbosd-text", OverlayCallback, NULL );
487     var_DelCallback( p_intf, "fbosd-font-size", OverlayCallback, NULL );
488     var_DelCallback( p_intf, "fbosd-font-color", OverlayCallback, NULL );
489     var_DelCallback( p_intf, "fbosd-font-opacity", OverlayCallback, NULL );
490     var_DelCallback( p_intf, "fbosd-clear", OverlayCallback, NULL );
491     var_DelCallback( p_intf, "fbosd-render", OverlayCallback, NULL );
492     var_DelCallback( p_intf, "fbosd-display", OverlayCallback, NULL );
493
494     var_Destroy( p_intf, "fbosd-x" );
495     var_Destroy( p_intf, "fbosd-y" );
496     var_Destroy( p_intf, "fbosd-position" );
497     var_Destroy( p_intf, "fbosd-image" );
498     var_Destroy( p_intf, "fbosd-text" );
499     var_Destroy( p_intf, "fbosd-font-size" );
500     var_Destroy( p_intf, "fbosd-font-color" );
501     var_Destroy( p_intf, "fbosd-font-opacity" );
502     var_Destroy( p_intf, "fbosd-clear" );
503     var_Destroy( p_intf, "fbosd-render" );
504     var_Destroy( p_intf, "fbosd-display" );
505
506     var_Destroy( p_intf, "fbosd-aspect-ratio" );
507
508     CloseDisplay( p_intf );
509
510     for( i = 0; i < FBOSD_RENDER_MAX; i++ )
511     {
512         free( p_sys->render[i].psz_string );
513         p_sys->render[i].i_state = FBOSD_STATE_FREE;
514     }
515
516 #if defined(FBOSD_BLENDING)
517     if( p_sys->p_blend ) CloseBlending( p_intf );
518 #endif
519     if( p_sys->p_text )  CloseTextRenderer( p_intf );
520
521     if( p_sys->p_image )
522         image_HandlerDelete( p_sys->p_image );
523     if( p_sys->p_overlay )
524         p_sys->p_overlay->pf_release( p_sys->p_overlay );
525
526     free( p_sys->p_style );
527     free( p_sys );
528 }
529
530 #if defined(FBOSD_BLENDING)
531 static int OpenBlending( intf_thread_t *p_intf )
532 {
533     if( p_intf->p_sys->p_blend ) return VLC_EGENERIC;
534
535     p_intf->p_sys->p_blend =
536             vlc_object_create( p_intf, sizeof(filter_t) );
537     vlc_object_attach( p_intf->p_sys->p_blend, p_intf );
538     p_intf->p_sys->p_blend->fmt_out.video.i_x_offset =
539         p_intf->p_sys->p_blend->fmt_out.video.i_y_offset = 0;
540     p_intf->p_sys->p_blend->fmt_out.video.i_aspect =
541             p_intf->p_sys->fmt_out.i_aspect;
542     p_intf->p_sys->p_blend->fmt_out.video.i_chroma =
543             p_intf->p_sys->fmt_out.i_chroma;
544     if( config_GetInt( p_intf, "freetype-yuvp" ) )
545         p_intf->p_sys->p_blend->fmt_in.video.i_chroma =
546                 VLC_FOURCC('Y','U','V','P');
547     else
548         p_intf->p_sys->p_blend->fmt_in.video.i_chroma =
549                 VLC_FOURCC('Y','U','V','A');
550
551     p_intf->p_sys->p_blend->p_module =
552         module_Need( p_intf->p_sys->p_blend, "video blending", 0, 0 );
553
554     if( !p_intf->p_sys->p_blend->p_module )
555         return VLC_EGENERIC;
556
557     return VLC_SUCCESS;
558 }
559
560 static void CloseBlending( intf_thread_t *p_intf )
561 {
562     if( p_intf->p_sys->p_blend )
563     {
564         if( p_intf->p_sys->p_blend->p_module )
565             module_Unneed( p_intf->p_sys->p_blend,
566                            p_intf->p_sys->p_blend->p_module );
567
568         vlc_object_detach( p_intf->p_sys->p_blend );
569         vlc_object_release( p_intf->p_sys->p_blend );
570     }
571 }
572 #endif
573
574 static int OpenTextRenderer( intf_thread_t *p_intf )
575 {
576     char *psz_modulename = NULL;
577
578     if( p_intf->p_sys->p_text ) return VLC_EGENERIC;
579
580     p_intf->p_sys->p_text =
581             vlc_object_create( p_intf, sizeof(filter_t) );
582     vlc_object_attach( p_intf->p_sys->p_text, p_intf );
583
584     p_intf->p_sys->p_text->fmt_out.video.i_width =
585         p_intf->p_sys->p_text->fmt_out.video.i_visible_width =
586         p_intf->p_sys->i_width;
587     p_intf->p_sys->p_text->fmt_out.video.i_height =
588         p_intf->p_sys->p_text->fmt_out.video.i_visible_height =
589         p_intf->p_sys->i_height;
590
591     psz_modulename = var_CreateGetString( p_intf, "text-renderer" );
592     if( psz_modulename && *psz_modulename )
593     {
594         p_intf->p_sys->p_text->p_module =
595             module_Need( p_intf->p_sys->p_text, "text renderer",
596                             psz_modulename, true );
597     }
598     if( !p_intf->p_sys->p_text->p_module )
599     {
600         p_intf->p_sys->p_text->p_module =
601             module_Need( p_intf->p_sys->p_text, "text renderer", 0, 0 );
602     }
603     free( psz_modulename );
604
605     if( !p_intf->p_sys->p_text->p_module )
606         return VLC_EGENERIC;
607
608     return VLC_SUCCESS;
609 }
610
611 static void CloseTextRenderer( intf_thread_t *p_intf )
612 {
613     if( p_intf->p_sys->p_text )
614     {
615         if( p_intf->p_sys->p_text->p_module )
616             module_Unneed( p_intf->p_sys->p_text,
617                            p_intf->p_sys->p_text->p_module );
618
619         vlc_object_detach( p_intf->p_sys->p_text );
620         vlc_object_release( p_intf->p_sys->p_text );
621     }
622 }
623
624 /*****************************************************************************
625  * AllocatePicture:
626  * allocate a picture buffer for use with the overlay fb.
627  *****************************************************************************/
628 static picture_t *AllocatePicture( vlc_object_t *p_this,
629                                    video_format_t *p_fmt )
630 {
631     picture_t *p_pic = malloc( sizeof( picture_t ) );
632     if( !p_pic ) return NULL;
633
634     if( !p_fmt->p_palette &&
635         ( p_fmt->i_chroma == VLC_FOURCC('Y','U','V','P') ) )
636     {
637         p_fmt->p_palette = malloc( sizeof(video_palette_t) );
638         if( !p_fmt->p_palette )
639         {
640             free( p_pic );
641             return NULL;
642         }
643     }
644     else p_fmt->p_palette = NULL;
645
646     p_pic->p_data_orig = NULL;
647
648     vout_AllocatePicture( p_this, p_pic, p_fmt->i_chroma,
649                           p_fmt->i_width, p_fmt->i_height, p_fmt->i_aspect );
650
651     if( !p_pic->i_planes )
652     {
653         free( p_pic );
654         free( p_fmt->p_palette );
655         return NULL;
656     }
657     return p_pic;
658 }
659
660 /*****************************************************************************
661  * DeAllocatePicture:
662  * Deallocate a picture buffer and free all associated memory.
663  *****************************************************************************/
664 static void DeAllocatePicture( vlc_object_t *p_this, picture_t *p_pic,
665                                video_format_t *p_fmt )
666 {
667     VLC_UNUSED(p_this);
668     if( p_pic )
669     {
670         free( p_pic->p_data_orig );
671         if( p_pic->pf_release ) p_pic->pf_release( p_pic );
672         else free( p_pic );
673     }
674     if( p_fmt )
675     {
676         free( p_fmt->p_palette );
677         p_fmt->p_palette = NULL;
678     }
679     p_pic = NULL;
680 }
681
682 /*****************************************************************************
683  * SetOverlayTransparency: Set the transparency for this overlay fb,
684  * - true is make transparent
685  * - false is make non tranparent
686  *****************************************************************************/
687 static void SetOverlayTransparency( intf_thread_t *p_intf,
688                                     bool b_transparent )
689 {
690     intf_sys_t *p_sys = (intf_sys_t *) p_intf->p_sys;
691     size_t i_size = p_sys->fmt_out.i_width * p_sys->fmt_out.i_height
692                         * p_sys->i_bytes_per_pixel;
693     size_t i_page_size = (p_sys->i_page_size > i_size) ?
694                             i_size : p_sys->i_page_size;
695
696     if( p_sys->p_overlay )
697     {
698         msg_Dbg( p_intf, "Make overlay %s",
699                  b_transparent ? "transparent" : "opaque" );
700         if( b_transparent )
701             memset( p_sys->p_overlay->p[0].p_pixels, 0xFF, i_page_size );
702         else
703             memset( p_sys->p_overlay->p[0].p_pixels, 0x00, i_page_size );
704     }
705 }
706
707 #if defined(FBOSD_BLENDING)
708 /*****************************************************************************
709  * BlendPicture: Blend two pictures together..
710  *****************************************************************************/
711 static int BlendPicture( intf_thread_t *p_intf, video_format_t *p_fmt_src,
712                          video_format_t *p_fmt_dst, picture_t *p_pic_src,
713                          picture_t *p_pic_dst )
714 {
715     intf_sys_t *p_sys = (intf_sys_t *) p_intf->p_sys;
716     if( p_sys->p_blend && p_sys->p_blend->p_module )
717     {
718         int i_x_offset = p_sys->i_x;
719         int i_y_offset = p_sys->i_y;
720
721         memcpy( &p_sys->p_blend->fmt_in.video, p_fmt_src, sizeof( video_format_t ) );
722
723         /* Update the output picture size */
724         p_sys->p_blend->fmt_out.video.i_width =
725             p_sys->p_blend->fmt_out.video.i_visible_width =
726                 p_fmt_dst->i_width;
727         p_sys->p_blend->fmt_out.video.i_height =
728             p_sys->p_blend->fmt_out.video.i_visible_height =
729                 p_fmt_dst->i_height;
730
731         i_x_offset = __MAX( i_x_offset, 0 );
732         i_y_offset = __MAX( i_y_offset, 0 );
733
734         p_sys->p_blend->pf_video_blend( p_sys->p_blend, p_pic_dst,
735             p_pic_src, p_pic_dst, i_x_offset, i_y_offset,
736             p_sys->i_alpha );
737
738         return VLC_SUCCESS;
739     }
740     return VLC_EGENERIC;
741 }
742
743 static int InvertAlpha( intf_thread_t *p_intf, picture_t **p_pic, video_format_t fmt )
744 {
745     uint8_t *p_begin = NULL, *p_end = NULL;
746     uint8_t i_skip = 0;
747
748     if( *p_pic && ((*p_pic)->i_planes != 1) )
749     {
750         msg_Err( p_intf,
751                  "cannot invert alpha channel too many planes %d (only 1 supported)",
752                  (*p_pic)->i_planes );
753         return VLC_EGENERIC;
754     }
755
756     switch( fmt.i_chroma )
757     {
758         case VLC_FOURCC('R','V','2','4'):
759             p_begin = (uint8_t *)(*p_pic)->p[Y_PLANE].p_pixels;
760             p_end   = (uint8_t *)(*p_pic)->p[Y_PLANE].p_pixels +
761                       ( fmt.i_height * (*p_pic)->p[Y_PLANE].i_pitch );
762             i_skip = 3;
763             break;
764         case VLC_FOURCC('R','V','3','2'):
765             p_begin = (uint8_t *)(*p_pic)->p[Y_PLANE].p_pixels;
766             p_end   = (uint8_t *)(*p_pic)->p[Y_PLANE].p_pixels +
767                       ( fmt.i_height * (*p_pic)->p[Y_PLANE].i_pitch );
768             i_skip = 4;
769             break;
770         default:
771             msg_Err( p_intf, "cannot invert alpha channel chroma not supported %4.4s",
772                     (char *)&fmt.i_chroma );
773             return VLC_EGENERIC;
774     }
775
776     for( ; p_begin < p_end; p_begin += i_skip )
777     {
778         uint8_t i_opacity = 0;
779
780         if( *p_begin != 0xFF )
781             i_opacity = 255 - *p_begin;
782         *p_begin = i_opacity;
783     }
784     /* end of kludge */
785     return VLC_SUCCESS;
786 }
787 #endif
788
789 /*****************************************************************************
790  * RenderPicture: Render the picture into the p_dest buffer.
791  * We don't take transparent pixels into account, so we don't have to blend
792  * the two images together.
793  *****************************************************************************/
794 static int RenderPicture( intf_thread_t *p_intf, int i_x_offset, int i_y_offset,
795                           picture_t *p_src, picture_t *p_dest )
796 {
797     int i;
798     VLC_UNUSED( p_intf );
799
800     if( !p_dest && !p_src ) return VLC_EGENERIC;
801
802     for( i = 0; i < p_src->i_planes ; i++ )
803     {
804         if( p_src->p[i].i_pitch == p_dest->p[i].i_pitch )
805         {
806             /* There are margins, but with the same width : perfect ! */
807             vlc_memcpy( p_dest->p[i].p_pixels, p_src->p[i].p_pixels,
808                         p_src->p[i].i_pitch * p_src->p[i].i_visible_lines );
809         }
810         else
811         {
812             /* We need to proceed line by line */
813             uint8_t *p_in  = p_src->p[i].p_pixels;
814             uint8_t *p_out = p_dest->p[i].p_pixels;
815
816             int i_x = i_x_offset * p_src->p[i].i_pixel_pitch;
817             int i_x_clip, i_y_clip;
818
819             /* Check boundaries, clip the image if necessary */
820             i_x_clip = ( i_x + p_src->p[i].i_visible_pitch ) - p_dest->p[i].i_visible_pitch;
821             i_x_clip = ( i_x_clip > 0 ) ? i_x_clip : 0;
822
823             i_y_clip = ( i_y_offset + p_src->p[i].i_visible_lines ) - p_dest->p[i].i_visible_lines;
824             i_y_clip = ( i_y_clip > 0 ) ? i_y_clip : 0;
825 #if defined(FBOSD_DEBUG)
826             msg_Dbg( p_intf, "i_pitch (%d,%d), (%d,%d)/(%d,%d)",
827                      p_dest->p[i].i_visible_pitch, p_src->p[i].i_visible_pitch,
828                      i_x_offset, i_y_offset, i_x, i_x_clip );
829 #endif
830             if( ( i_y_offset <= p_dest->p[i].i_visible_lines ) &&
831                 ( i_x <= p_dest->p[i].i_visible_pitch ) )
832             {
833                 int i_line;
834
835                 p_out += ( i_y_offset * p_dest->p[i].i_pitch );
836                 for( i_line = 0; i_line < ( p_src->p[i].i_visible_lines - i_y_clip ); i_line++ )
837                 {
838                     vlc_memcpy( p_out + i_x, p_in,
839                                 p_src->p[i].i_visible_pitch - i_x_clip );
840                     p_in += p_src->p[i].i_pitch;
841                     p_out += p_dest->p[i].i_pitch;
842                 }
843             }
844         }
845     }
846     return VLC_SUCCESS;
847 }
848
849 /*****************************************************************************
850  * RenderText - Render text to the desired picture format
851  *****************************************************************************/
852 static picture_t *RenderText( intf_thread_t *p_intf, const char *psz_string,
853                               text_style_t *p_style, video_format_t *p_fmt )
854 {
855     intf_sys_t *p_sys = (intf_sys_t *) p_intf->p_sys;
856     subpicture_region_t *p_region;
857     picture_t *p_dest = NULL;
858
859     if( !psz_string ) return p_dest;
860
861     if( p_sys->p_text && p_sys->p_text->p_module )
862     {
863         p_region = (subpicture_region_t *) malloc( sizeof(subpicture_region_t) );
864         if( !p_region )
865             return p_dest;
866
867         memset( p_region, 0, sizeof(subpicture_region_t) );
868
869         p_region->psz_text = strdup( psz_string );
870         if( !p_region->psz_text )
871         {
872             free( p_region );
873             return NULL;
874         }
875         p_region->p_style = p_style;
876
877         p_region->fmt.i_chroma = VLC_FOURCC('T','E','X','T');
878         p_region->fmt.i_aspect = 0;
879         p_region->fmt.i_width = p_region->fmt.i_visible_width = 0;
880         p_region->fmt.i_height = p_region->fmt.i_visible_height = 0;
881         p_region->fmt.i_x_offset = 0;
882         p_region->fmt.i_y_offset = 0;
883
884         p_region->i_align = OSD_ALIGN_LEFT | OSD_ALIGN_TOP;
885
886         if( p_sys->p_text->pf_render_text )
887         {
888             video_format_t fmt_out;
889
890             memset( &fmt_out, 0, sizeof(video_format_t) );
891
892             p_sys->p_text->pf_render_text( p_sys->p_text,
893                                            p_region, p_region );
894
895 #if defined(FBOSD_BLENDING)
896             fmt_out = p_region->fmt;
897             fmt_out.i_bits_per_pixel = 32;
898             vlc_memcpy( p_fmt, &fmt_out, sizeof(video_format_t) );
899
900             p_dest = AllocatePicture( VLC_OBJECT(p_intf), &fmt_out );
901             if( !p_dest )
902             {
903                 if( p_region->picture.pf_release )
904                     p_region->picture.pf_release( &p_region->picture );
905                 free( p_region->psz_text );
906                 free( p_region );
907                 return NULL;
908             }
909             vout_CopyPicture( VLC_OBJECT(p_intf), p_dest, &p_region->picture );
910 #else
911             fmt_out.i_chroma = p_fmt->i_chroma;
912             p_dest = ConvertImage( p_intf, &p_region->picture,
913                                    &p_region->fmt, &fmt_out );
914 #endif
915             if( p_region->picture.pf_release )
916                 p_region->picture.pf_release( &p_region->picture );
917             free( p_region->psz_text );
918             free( p_region );
919             return p_dest;
920         }
921         free( p_region->psz_text );
922         free( p_region );
923     }
924     return p_dest;
925 }
926
927 /*****************************************************************************
928  * LoadImage: Load an image from file into a picture buffer.
929  *****************************************************************************/
930 static picture_t *LoadImage( intf_thread_t *p_intf, video_format_t *p_fmt,
931                              char *psz_file )
932 {
933     picture_t  *p_pic = NULL;
934
935     if( psz_file && p_intf->p_sys->p_image )
936     {
937         video_format_t fmt_in, fmt_out;
938
939         memset( &fmt_in, 0, sizeof(fmt_in) );
940         memset( &fmt_out, 0, sizeof(fmt_out) );
941
942         fmt_out.i_chroma = p_fmt->i_chroma;
943         p_pic = image_ReadUrl( p_intf->p_sys->p_image, psz_file,
944                                &fmt_in, &fmt_out );
945
946         msg_Dbg( p_intf, "image size %dx%d chroma %4.4s",
947                  fmt_out.i_width, fmt_out.i_height,
948                  (char *)&p_fmt->i_chroma );
949     }
950     return p_pic;
951 }
952
953 #if ! defined(FBOSD_BLENDING)
954 /*****************************************************************************
955  * Convertmage: Convert image to another fourcc
956  *****************************************************************************/
957 static picture_t *ConvertImage( intf_thread_t *p_intf, picture_t *p_pic,
958                          video_format_t *p_fmt_in, video_format_t *p_fmt_out )
959 {
960     intf_sys_t *p_sys = (intf_sys_t *) p_intf->p_sys;
961     picture_t  *p_old = NULL;
962
963     if( p_sys->p_image )
964     {
965         p_old = image_Convert( p_sys->p_image, p_pic, p_fmt_in, p_fmt_out );
966
967         msg_Dbg( p_intf, "converted image size %dx%d chroma %4.4s",
968                  p_fmt_out->i_width, p_fmt_out->i_height,
969                  (char *)&p_fmt_out->i_chroma );
970     }
971     return p_old;
972 }
973 #endif
974
975 /*****************************************************************************
976  * Init: initialize framebuffer video thread output method
977  *****************************************************************************/
978 static int Init( intf_thread_t *p_intf )
979 {
980     intf_sys_t *p_sys = (intf_sys_t *) p_intf->p_sys;
981
982     /* Initialize the output structure: RGB with square pixels, whatever
983      * the input format is, since it's the only format we know */
984     switch( p_sys->var_info.bits_per_pixel )
985     {
986     case 8: /* FIXME: set the palette */
987         p_sys->fmt_out.i_chroma = VLC_FOURCC('R','G','B','2'); break;
988     case 15:
989         p_sys->fmt_out.i_chroma = VLC_FOURCC('R','V','1','5'); break;
990     case 16:
991         p_sys->fmt_out.i_chroma = VLC_FOURCC('R','V','1','6'); break;
992     case 24:
993         p_sys->fmt_out.i_chroma = VLC_FOURCC('R','V','2','4'); break;
994     case 32:
995         p_sys->fmt_out.i_chroma = VLC_FOURCC('R','V','3','2'); break;
996     default:
997         msg_Err( p_intf, "unknown screen depth %i",
998                  p_sys->var_info.bits_per_pixel );
999         return VLC_EGENERIC;
1000     }
1001
1002     p_sys->fmt_out.i_bits_per_pixel = p_sys->var_info.bits_per_pixel;
1003     p_sys->fmt_out.i_width  = p_sys->i_width;
1004     p_sys->fmt_out.i_height = p_sys->i_height;
1005
1006     /* Assume we have square pixels */
1007     if( p_sys->i_aspect < 0 )
1008     {
1009         p_sys->fmt_out.i_aspect = ( p_sys->i_width
1010                                   * VOUT_ASPECT_FACTOR ) / p_sys->i_height;
1011     }
1012     else p_sys->fmt_out.i_aspect = p_sys->i_aspect;
1013
1014     p_sys->fmt_out.i_sar_num = p_sys->fmt_out.i_sar_den = 1;
1015
1016     /* Allocate overlay buffer */
1017     p_sys->p_overlay = AllocatePicture( VLC_OBJECT(p_intf),
1018                                         &p_sys->fmt_out );
1019     if( !p_sys->p_overlay ) return VLC_EGENERIC;
1020
1021     SetOverlayTransparency( p_intf, true );
1022
1023     /* We know the chroma, allocate a buffer which will be used
1024      * to write to the overlay framebuffer */
1025     p_sys->p_overlay->p->i_pixel_pitch = p_sys->i_bytes_per_pixel;
1026     p_sys->p_overlay->p->i_lines = p_sys->var_info.yres;
1027     p_sys->p_overlay->p->i_visible_lines = p_sys->var_info.yres;
1028
1029     if( p_sys->var_info.xres_virtual )
1030     {
1031         p_sys->p_overlay->p->i_pitch = p_sys->var_info.xres_virtual
1032                              * p_sys->i_bytes_per_pixel;
1033     }
1034     else
1035     {
1036         p_sys->p_overlay->p->i_pitch = p_sys->var_info.xres
1037                              * p_sys->i_bytes_per_pixel;
1038     }
1039
1040     p_sys->p_overlay->p->i_visible_pitch = p_sys->var_info.xres
1041                                  * p_sys->i_bytes_per_pixel;
1042
1043     p_sys->p_overlay->i_planes = 1;
1044
1045     return VLC_SUCCESS;
1046 }
1047
1048 /*****************************************************************************
1049  * End: terminate framebuffer interface
1050  *****************************************************************************/
1051 static void End( intf_thread_t *p_intf )
1052 {
1053     intf_sys_t *p_sys = (intf_sys_t *) p_intf->p_sys;
1054
1055     /* CleanUp */
1056     SetOverlayTransparency( p_intf, false );
1057     if( p_sys->p_overlay )
1058     {
1059         int ret;
1060         ret = write( p_sys->i_fd, p_sys->p_overlay->p[0].p_pixels,
1061                      p_sys->i_page_size );
1062         if( ret < 0 )
1063             msg_Err( p_intf, "unable to clear overlay" );
1064     }
1065
1066     DeAllocatePicture( VLC_OBJECT(p_intf), p_intf->p_sys->p_overlay,
1067                        &p_intf->p_sys->fmt_out );
1068     p_intf->p_sys->p_overlay = NULL;
1069 }
1070
1071 /*****************************************************************************
1072  * OpenDisplay: initialize framebuffer
1073  *****************************************************************************/
1074 static int OpenDisplay( intf_thread_t *p_intf )
1075 {
1076     intf_sys_t *p_sys = (intf_sys_t *) p_intf->p_sys;
1077     char *psz_device;                             /* framebuffer device path */
1078     struct fb_fix_screeninfo    fix_info;     /* framebuffer fix information */
1079
1080     /* Open framebuffer device */
1081     if( !(psz_device = config_GetPsz( p_intf, "fbosd-dev" )) )
1082     {
1083         msg_Err( p_intf, "don't know which fb osd/overlay device to open" );
1084         return VLC_EGENERIC;
1085     }
1086
1087     p_sys->i_fd = open( psz_device, O_RDWR );
1088     if( p_sys->i_fd == -1 )
1089     {
1090         msg_Err( p_intf, "cannot open %s (%s)", psz_device, strerror(errno) );
1091         free( psz_device );
1092         return VLC_EGENERIC;
1093     }
1094     free( psz_device );
1095
1096     /* Get framebuffer device information */
1097     if( ioctl( p_sys->i_fd, FBIOGET_VSCREENINFO, &p_sys->var_info ) )
1098     {
1099         msg_Err( p_intf, "cannot get fb info (%s)", strerror(errno) );
1100         close( p_sys->i_fd );
1101         return VLC_EGENERIC;
1102     }
1103
1104     /* Get some info on the framebuffer itself */
1105     if( ioctl( p_sys->i_fd, FBIOGET_FSCREENINFO, &fix_info ) == 0 )
1106     {
1107         p_sys->i_width = p_sys->fmt_out.i_width = p_sys->var_info.xres;
1108         p_sys->i_height = p_sys->fmt_out.i_height = p_sys->var_info.yres;
1109     }
1110
1111     /* FIXME: if the image is full-size, it gets cropped on the left
1112      * because of the xres / xres_virtual slight difference */
1113     msg_Dbg( p_intf, "%ix%i (virtual %ix%i)",
1114              p_sys->var_info.xres, p_sys->var_info.yres,
1115              p_sys->var_info.xres_virtual,
1116              p_sys->var_info.yres_virtual );
1117
1118     p_sys->fmt_out.i_width = p_sys->i_width;
1119     p_sys->fmt_out.i_height = p_sys->i_height;
1120
1121     p_sys->p_palette = NULL;
1122     p_sys->b_pan = ( fix_info.ypanstep || fix_info.ywrapstep );
1123
1124     switch( p_sys->var_info.bits_per_pixel )
1125     {
1126     case 8:
1127         p_sys->p_palette = malloc( 8 * 256 * sizeof( uint16_t ) );
1128         if( !p_sys->p_palette )
1129         {
1130             close( p_sys->i_fd );
1131             return VLC_ENOMEM;
1132         }
1133         p_sys->fb_cmap.start = 0;
1134         p_sys->fb_cmap.len = 256;
1135         p_sys->fb_cmap.red = p_sys->p_palette;
1136         p_sys->fb_cmap.green  = p_sys->p_palette + 256 * sizeof( uint16_t );
1137         p_sys->fb_cmap.blue   = p_sys->p_palette + 2 * 256 * sizeof( uint16_t );
1138         p_sys->fb_cmap.transp = p_sys->p_palette + 3 * 256 * sizeof( uint16_t );
1139
1140         /* Save the colormap */
1141         ioctl( p_sys->i_fd, FBIOGETCMAP, &p_sys->fb_cmap );
1142
1143         p_sys->i_bytes_per_pixel = 1;
1144         break;
1145
1146     case 15:
1147     case 16:
1148         p_sys->i_bytes_per_pixel = 2;
1149         break;
1150
1151     case 24:
1152         p_sys->i_bytes_per_pixel = 3;
1153         break;
1154
1155     case 32:
1156         p_sys->i_bytes_per_pixel = 4;
1157         break;
1158
1159     default:
1160         msg_Err( p_intf, "screen depth %d is not supported",
1161                          p_sys->var_info.bits_per_pixel );
1162
1163         close( p_sys->i_fd );
1164         return VLC_EGENERIC;
1165     }
1166
1167     p_sys->i_page_size = p_sys->i_width * p_sys->i_height
1168                          * p_sys->i_bytes_per_pixel;
1169
1170     msg_Dbg( p_intf, "framebuffer type=%d, visual=%d, ypanstep=%d, "
1171              "ywrap=%d, accel=%d", fix_info.type, fix_info.visual,
1172              fix_info.ypanstep, fix_info.ywrapstep, fix_info.accel );
1173     return VLC_SUCCESS;
1174 }
1175
1176 /*****************************************************************************
1177  * CloseDisplay: terminate FB interface thread
1178  *****************************************************************************/
1179 static void CloseDisplay( intf_thread_t *p_intf )
1180 {
1181     intf_sys_t *p_sys = (intf_sys_t *) p_intf;
1182
1183     /* Restore palette */
1184     if( p_sys->var_info.bits_per_pixel == 8 )
1185     {
1186         ioctl( p_sys->i_fd, FBIOPUTCMAP, &p_sys->fb_cmap );
1187         free( p_sys->p_palette );
1188         p_sys->p_palette = NULL;
1189     }
1190
1191     /* Close fb */
1192     close( p_sys->i_fd );
1193 }
1194
1195 static void Render( intf_thread_t *p_intf, struct fbosd_render_t *render )
1196 {
1197     intf_sys_t *p_sys = (intf_sys_t*) p_intf->p_sys;
1198
1199     if( render->i_state != FBOSD_STATE_RENDER ) return;
1200     if( !render->psz_string ) return;
1201
1202     if( render->i_type == FBOSD_RENDER_IMAGE )
1203     {
1204         picture_t *p_pic;
1205         p_pic = LoadImage( p_intf, &p_sys->fmt_out, render->psz_string );
1206         if( p_pic )
1207         {
1208             RenderPicture( p_intf, render->i_x, render->i_y,
1209                            p_pic, p_sys->p_overlay );
1210             p_pic->pf_release( p_pic );
1211         }
1212     }
1213     else if( render->i_type == FBOSD_RENDER_TEXT )
1214     {
1215         picture_t *p_text;
1216 #if defined(FBOSD_BLENDING)
1217         video_format_t fmt_in;
1218         memset( &fmt_in, 0, sizeof(video_format_t) );
1219         p_text = RenderText( p_intf, render->psz_string, &render->text_style,
1220                              &fmt_in );
1221         if( p_text )
1222         {
1223             BlendPicture( p_intf, &fmt_in, &p_sys->fmt_out,
1224                           p_text, p_sys->p_overlay );
1225             msg_Dbg( p_intf, "releasing picture" );
1226             DeAllocatePicture( VLC_OBJECT( p_intf ), p_text, &fmt_in );
1227         }
1228 #else
1229         p_text = RenderText( p_intf, render->psz_string, &render->text_style,
1230                              &p_sys->fmt_out );
1231         if( p_text )
1232         {
1233             RenderPicture( p_intf, render->i_x, render->i_y,
1234                            p_text, p_sys->p_overlay );
1235             p_text->pf_release( p_text );
1236         }
1237 #endif
1238     }
1239 }
1240
1241 static void RenderClear( intf_thread_t *p_intf, struct fbosd_render_t *render )
1242 {
1243     intf_sys_t *p_sys = (intf_sys_t*) p_intf->p_sys;
1244
1245     vlc_memcpy( &render->text_style, &default_text_style,
1246                 sizeof( text_style_t ) );
1247     free( render->psz_string );
1248     render->psz_string = NULL;
1249
1250     render->i_x = p_sys->i_x;
1251     render->i_y = p_sys->i_y;
1252     render->i_pos = p_sys->i_pos;
1253     render->i_alpha = p_sys->i_alpha;
1254     render->b_absolute = p_sys->b_absolute;
1255     render->i_state = FBOSD_STATE_FREE;
1256 }
1257
1258 static bool isRendererReady( intf_thread_t *p_intf )
1259 {
1260     intf_sys_t *p_sys = (intf_sys_t*) p_intf->p_sys;
1261     int i;
1262
1263     /* Check if there are more items to render */
1264     for( i = 0; i < FBOSD_RENDER_MAX; i++ )
1265     {
1266         if( p_sys->render[i].i_state == FBOSD_STATE_RESERVED )
1267             return false;
1268     }
1269     return true;
1270 }
1271
1272 /*****************************************************************************
1273  * Run: thread
1274  *****************************************************************************
1275  * This part of the interface is in a separate thread so that we can call
1276  * exec() from within it without annoying the rest of the program.
1277  *****************************************************************************/
1278 static void Run( intf_thread_t *p_intf )
1279 {
1280     intf_sys_t *p_sys = (intf_sys_t*) p_intf->p_sys;
1281
1282     while( !intf_ShouldDie( p_intf ) )
1283     {
1284         int i;
1285
1286         /* Is there somthing to render? */
1287         for( i = 0; i < FBOSD_RENDER_MAX; i++ )
1288         {
1289             if( p_sys->render[i].i_state == FBOSD_STATE_RENDER )
1290             {
1291                 Render( p_intf, &p_sys->render[i] );
1292                 RenderClear( p_intf, &p_sys->render[i] );
1293             }
1294         }
1295
1296         if( p_sys->b_clear )
1297         {
1298             SetOverlayTransparency( p_intf, true );
1299
1300             var_SetString( p_intf, "fbosd-image", "" );
1301             var_SetString( p_intf, "fbosd-text", "" );
1302
1303             p_sys->b_clear = false;
1304             p_sys->b_need_update = true;
1305         }
1306
1307         if( p_sys->b_need_update && p_sys->p_overlay &&
1308             isRendererReady( p_intf ) )
1309         {
1310             int ret;
1311 #if defined(FBOSD_BLENDING)
1312             /* Reverse alpha channel to work around FPGA bug */
1313             InvertAlpha( p_intf, &p_sys->p_overlay, p_sys->fmt_out );
1314 #endif
1315             ret = write( p_sys->i_fd, p_sys->p_overlay->p[0].p_pixels,
1316                          p_sys->i_page_size );
1317             if( ret < 0 )
1318                 msg_Err( p_intf, "unable to write to overlay" );
1319             lseek( p_sys->i_fd, 0, SEEK_SET );
1320
1321             /* clear the picture */
1322             memset( p_sys->p_overlay->p[0].p_pixels, 0xFF, p_sys->i_page_size );
1323             p_sys->b_need_update = false;
1324         }
1325
1326         if( vlc_CPU() & CPU_CAPABILITY_FPU )
1327             msleep( INTF_IDLE_SLEEP );
1328         else
1329             msleep( 500 );
1330     }
1331
1332     End( p_intf );
1333 }
1334
1335 static int OverlayCallback( vlc_object_t *p_this, char const *psz_cmd,
1336                  vlc_value_t oldval, vlc_value_t newval, void *p_data )
1337 {
1338     intf_thread_t *p_intf = (intf_thread_t *) p_this;
1339     intf_sys_t *p_sys = (intf_sys_t*) p_intf->p_sys;
1340     VLC_UNUSED(oldval); VLC_UNUSED(p_data);
1341
1342     if( !strncmp( psz_cmd, "fbosd-display", 13 ) )
1343     {
1344         p_sys->b_need_update = true;
1345     }
1346     else if( !strncmp( psz_cmd, "fbosd-clear", 11 ) )
1347     {
1348         int i;
1349         /* Clear the entire render list */
1350         for( i = 0; i < FBOSD_RENDER_MAX; i++ )
1351         {
1352             RenderClear( p_intf, &p_sys->render[i] );
1353         }
1354         p_sys->b_clear = true;
1355     }
1356     else if( !strncmp( psz_cmd, "fbosd-render", 12 ) )
1357     {
1358         int i;
1359         /* Are we already busy with on slot ? */
1360         for( i = 0; i < FBOSD_RENDER_MAX; i++ )
1361         {
1362             if( p_sys->render[i].i_state == FBOSD_STATE_RESERVED )
1363             {
1364                 p_sys->render[i].i_state = FBOSD_STATE_RENDER;
1365                 break;
1366             }
1367         }
1368     }
1369     else
1370     {
1371         int i;
1372         /* Are we already busy with on slot ? */
1373         for( i = 0; i < FBOSD_RENDER_MAX; i++ )
1374         {
1375             if( p_sys->render[i].i_state == FBOSD_STATE_RESERVED )
1376                 break;
1377         }
1378         /* No, then find first FREE slot */
1379         if( p_sys->render[i].i_state != FBOSD_STATE_RESERVED )
1380         {
1381             for( i = 0; i < FBOSD_RENDER_MAX; i++ )
1382             {
1383                 if( p_sys->render[i].i_state == FBOSD_STATE_FREE )
1384                     break;
1385             }
1386             if( p_sys->render[i].i_state != FBOSD_STATE_FREE )
1387             {
1388                 msg_Warn( p_this, "render space depleated" );
1389                 return VLC_SUCCESS;
1390             }
1391         }
1392         /* Found a free slot */
1393         p_sys->render[i].i_state = FBOSD_STATE_RESERVED;
1394         if( !strncmp( psz_cmd, "fbosd-image", 11 ) )
1395         {
1396             free( p_sys->render[i].psz_string );
1397             p_sys->render[i].psz_string = strdup( newval.psz_string );
1398             p_sys->render[i].i_type = FBOSD_RENDER_IMAGE;
1399         }
1400         else if( !strncmp( psz_cmd, "fbosd-text", 10 ) )
1401         {
1402             free( p_sys->render[i].psz_string );
1403             p_sys->render[i].psz_string = strdup( newval.psz_string );
1404             p_sys->render[i].i_type = FBOSD_RENDER_TEXT;
1405         }
1406         else if( !strncmp( psz_cmd, "fbosd-x", 7 ) )
1407         {
1408             p_sys->render[i].b_absolute = false;
1409             p_sys->render[i].i_x = (newval.i_int < p_sys->i_width) ?
1410                                     newval.i_int : p_sys->i_width;
1411         }
1412         else if( !strncmp( psz_cmd, "fbosd-y", 7 ) )
1413         {
1414             p_sys->render[i].b_absolute = false;
1415             p_sys->render[i].i_y = (newval.i_int < p_sys->i_height) ?
1416                                     newval.i_int : p_sys->i_height;
1417         }
1418         else if( !strncmp( psz_cmd, "fbosd-position", 14 ) )
1419         {
1420             p_sys->render[i].b_absolute = true;
1421             p_sys->render[i].i_pos = newval.i_int;
1422         }
1423         else if( !strncmp( psz_cmd, "fbosd-font-size", 15 ) )
1424         {
1425             p_sys->render[i].text_style.i_font_size = newval.i_int;
1426         }
1427         else if( !strncmp( psz_cmd, "fbosd-font-color", 16 ) )
1428         {
1429             p_sys->render[i].text_style.i_font_color = newval.i_int;
1430         }
1431         else if( !strncmp( psz_cmd, "fbosd-font-opacity", 18 ) )
1432         {
1433             p_sys->render[i].text_style.i_font_alpha = 255 - newval.i_int;
1434         }
1435         else if( !strncmp( psz_cmd, "fbosd-alpha", 11 ) )
1436         {
1437             p_sys->render[i].i_alpha = newval.i_int;
1438         }
1439     }
1440     return VLC_SUCCESS;
1441 }