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