Pull cancellation into (most) remaining interfaces
[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( 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, NULL );
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, NULL );
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         picture_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_picture = picture_New( p_fmt->i_chroma,
632                                         p_fmt->i_width, p_fmt->i_height,
633                                         p_fmt->i_aspect );
634     if( !p_picture )
635         return NULL;
636
637     if( !p_fmt->p_palette &&
638         ( p_fmt->i_chroma == VLC_FOURCC('Y','U','V','P') ) )
639     {
640         p_fmt->p_palette = malloc( sizeof(video_palette_t) );
641         if( !p_fmt->p_palette )
642         {
643             picture_Release( p_picture );
644             return NULL;
645         }
646     }
647     else
648     {
649         p_fmt->p_palette = NULL;
650     }
651
652     return p_picture;
653 }
654
655 /*****************************************************************************
656  * DeAllocatePicture:
657  * Deallocate a picture buffer and free all associated memory.
658  *****************************************************************************/
659 static void DeAllocatePicture( vlc_object_t *p_this, picture_t *p_pic,
660                                video_format_t *p_fmt )
661 {
662     VLC_UNUSED(p_this);
663
664     if( p_fmt )
665     {
666         free( p_fmt->p_palette );
667         p_fmt->p_palette = NULL;
668     }
669
670     if( p_pic )
671         picture_Release( p_pic );
672 }
673
674 /*****************************************************************************
675  * SetOverlayTransparency: Set the transparency for this overlay fb,
676  * - true is make transparent
677  * - false is make non tranparent
678  *****************************************************************************/
679 static void SetOverlayTransparency( intf_thread_t *p_intf,
680                                     bool b_transparent )
681 {
682     intf_sys_t *p_sys = (intf_sys_t *) p_intf->p_sys;
683     size_t i_size = p_sys->fmt_out.i_width * p_sys->fmt_out.i_height
684                         * p_sys->i_bytes_per_pixel;
685     size_t i_page_size = (p_sys->i_page_size > i_size) ?
686                             i_size : p_sys->i_page_size;
687
688     if( p_sys->p_overlay )
689     {
690         msg_Dbg( p_intf, "Make overlay %s",
691                  b_transparent ? "transparent" : "opaque" );
692         if( b_transparent )
693             memset( p_sys->p_overlay->p[0].p_pixels, 0xFF, i_page_size );
694         else
695             memset( p_sys->p_overlay->p[0].p_pixels, 0x00, i_page_size );
696     }
697 }
698
699 #if defined(FBOSD_BLENDING)
700 /*****************************************************************************
701  * BlendPicture: Blend two pictures together..
702  *****************************************************************************/
703 static int BlendPicture( intf_thread_t *p_intf, video_format_t *p_fmt_src,
704                          video_format_t *p_fmt_dst, picture_t *p_pic_src,
705                          picture_t *p_pic_dst )
706 {
707     intf_sys_t *p_sys = (intf_sys_t *) p_intf->p_sys;
708     if( p_sys->p_blend && p_sys->p_blend->p_module )
709     {
710         int i_x_offset = p_sys->i_x;
711         int i_y_offset = p_sys->i_y;
712
713         memcpy( &p_sys->p_blend->fmt_in.video, p_fmt_src, sizeof( video_format_t ) );
714
715         /* Update the output picture size */
716         p_sys->p_blend->fmt_out.video.i_width =
717             p_sys->p_blend->fmt_out.video.i_visible_width =
718                 p_fmt_dst->i_width;
719         p_sys->p_blend->fmt_out.video.i_height =
720             p_sys->p_blend->fmt_out.video.i_visible_height =
721                 p_fmt_dst->i_height;
722
723         i_x_offset = __MAX( i_x_offset, 0 );
724         i_y_offset = __MAX( i_y_offset, 0 );
725
726         p_sys->p_blend->pf_video_blend( p_sys->p_blend, p_pic_dst,
727             p_pic_src, p_pic_dst, i_x_offset, i_y_offset,
728             p_sys->i_alpha );
729
730         return VLC_SUCCESS;
731     }
732     return VLC_EGENERIC;
733 }
734
735 static int InvertAlpha( intf_thread_t *p_intf, picture_t **p_pic, video_format_t fmt )
736 {
737     uint8_t *p_begin = NULL, *p_end = NULL;
738     uint8_t i_skip = 0;
739
740     if( *p_pic && ((*p_pic)->i_planes != 1) )
741     {
742         msg_Err( p_intf,
743                  "cannot invert alpha channel too many planes %d (only 1 supported)",
744                  (*p_pic)->i_planes );
745         return VLC_EGENERIC;
746     }
747
748     switch( fmt.i_chroma )
749     {
750         case VLC_FOURCC('R','V','2','4'):
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 = 3;
755             break;
756         case VLC_FOURCC('R','V','3','2'):
757             p_begin = (uint8_t *)(*p_pic)->p[Y_PLANE].p_pixels;
758             p_end   = (uint8_t *)(*p_pic)->p[Y_PLANE].p_pixels +
759                       ( fmt.i_height * (*p_pic)->p[Y_PLANE].i_pitch );
760             i_skip = 4;
761             break;
762         default:
763             msg_Err( p_intf, "cannot invert alpha channel chroma not supported %4.4s",
764                     (char *)&fmt.i_chroma );
765             return VLC_EGENERIC;
766     }
767
768     for( ; p_begin < p_end; p_begin += i_skip )
769     {
770         uint8_t i_opacity = 0;
771
772         if( *p_begin != 0xFF )
773             i_opacity = 255 - *p_begin;
774         *p_begin = i_opacity;
775     }
776     /* end of kludge */
777     return VLC_SUCCESS;
778 }
779 #endif
780
781 /*****************************************************************************
782  * RenderPicture: Render the picture into the p_dest buffer.
783  * We don't take transparent pixels into account, so we don't have to blend
784  * the two images together.
785  *****************************************************************************/
786 static int RenderPicture( intf_thread_t *p_intf, int i_x_offset, int i_y_offset,
787                           picture_t *p_src, picture_t *p_dest )
788 {
789     int i;
790     VLC_UNUSED( p_intf );
791
792     if( !p_dest && !p_src ) return VLC_EGENERIC;
793
794     for( i = 0; i < p_src->i_planes ; i++ )
795     {
796         if( p_src->p[i].i_pitch == p_dest->p[i].i_pitch )
797         {
798             /* There are margins, but with the same width : perfect ! */
799             vlc_memcpy( p_dest->p[i].p_pixels, p_src->p[i].p_pixels,
800                         p_src->p[i].i_pitch * p_src->p[i].i_visible_lines );
801         }
802         else
803         {
804             /* We need to proceed line by line */
805             uint8_t *p_in  = p_src->p[i].p_pixels;
806             uint8_t *p_out = p_dest->p[i].p_pixels;
807
808             int i_x = i_x_offset * p_src->p[i].i_pixel_pitch;
809             int i_x_clip, i_y_clip;
810
811             /* Check boundaries, clip the image if necessary */
812             i_x_clip = ( i_x + p_src->p[i].i_visible_pitch ) - p_dest->p[i].i_visible_pitch;
813             i_x_clip = ( i_x_clip > 0 ) ? i_x_clip : 0;
814
815             i_y_clip = ( i_y_offset + p_src->p[i].i_visible_lines ) - p_dest->p[i].i_visible_lines;
816             i_y_clip = ( i_y_clip > 0 ) ? i_y_clip : 0;
817 #if defined(FBOSD_DEBUG)
818             msg_Dbg( p_intf, "i_pitch (%d,%d), (%d,%d)/(%d,%d)",
819                      p_dest->p[i].i_visible_pitch, p_src->p[i].i_visible_pitch,
820                      i_x_offset, i_y_offset, i_x, i_x_clip );
821 #endif
822             if( ( i_y_offset <= p_dest->p[i].i_visible_lines ) &&
823                 ( i_x <= p_dest->p[i].i_visible_pitch ) )
824             {
825                 int i_line;
826
827                 p_out += ( i_y_offset * p_dest->p[i].i_pitch );
828                 for( i_line = 0; i_line < ( p_src->p[i].i_visible_lines - i_y_clip ); i_line++ )
829                 {
830                     vlc_memcpy( p_out + i_x, p_in,
831                                 p_src->p[i].i_visible_pitch - i_x_clip );
832                     p_in += p_src->p[i].i_pitch;
833                     p_out += p_dest->p[i].i_pitch;
834                 }
835             }
836         }
837     }
838     return VLC_SUCCESS;
839 }
840
841 /*****************************************************************************
842  * RenderText - Render text to the desired picture format
843  *****************************************************************************/
844 static picture_t *RenderText( intf_thread_t *p_intf, const char *psz_string,
845                               text_style_t *p_style, video_format_t *p_fmt )
846 {
847     intf_sys_t *p_sys = (intf_sys_t *) p_intf->p_sys;
848     subpicture_region_t *p_region;
849     picture_t *p_dest = NULL;
850
851     if( !psz_string ) return p_dest;
852
853     if( p_sys->p_text && p_sys->p_text->p_module )
854     {
855         p_region = (subpicture_region_t *) malloc( sizeof(subpicture_region_t) );
856         if( !p_region )
857             return p_dest;
858
859         memset( p_region, 0, sizeof(subpicture_region_t) );
860
861         p_region->psz_text = strdup( psz_string );
862         if( !p_region->psz_text )
863         {
864             free( p_region );
865             return NULL;
866         }
867         p_region->p_style = p_style;
868
869         p_region->fmt.i_chroma = VLC_FOURCC('T','E','X','T');
870         p_region->fmt.i_aspect = 0;
871         p_region->fmt.i_width = p_region->fmt.i_visible_width = 0;
872         p_region->fmt.i_height = p_region->fmt.i_visible_height = 0;
873         p_region->fmt.i_x_offset = 0;
874         p_region->fmt.i_y_offset = 0;
875
876         p_region->i_align = OSD_ALIGN_LEFT | OSD_ALIGN_TOP;
877
878         if( p_sys->p_text->pf_render_text )
879         {
880             video_format_t fmt_out;
881
882             memset( &fmt_out, 0, sizeof(video_format_t) );
883
884             p_sys->p_text->pf_render_text( p_sys->p_text,
885                                            p_region, p_region );
886
887 #if defined(FBOSD_BLENDING)
888             fmt_out = p_region->fmt;
889             fmt_out.i_bits_per_pixel = 32;
890             vlc_memcpy( p_fmt, &fmt_out, sizeof(video_format_t) );
891
892             p_dest = AllocatePicture( VLC_OBJECT(p_intf), &fmt_out );
893             if( !p_dest )
894             {
895                 if( p_region->picture.pf_release )
896                     p_region->picture.pf_release( &p_region->picture );
897                 free( p_region->psz_text );
898                 free( p_region );
899                 return NULL;
900             }
901             vout_CopyPicture( VLC_OBJECT(p_intf), p_dest, &p_region->picture );
902 #else
903             fmt_out.i_chroma = p_fmt->i_chroma;
904             p_dest = ConvertImage( p_intf, &p_region->picture,
905                                    &p_region->fmt, &fmt_out );
906 #endif
907             if( p_region->picture.pf_release )
908                 p_region->picture.pf_release( &p_region->picture );
909             free( p_region->psz_text );
910             free( p_region );
911             return p_dest;
912         }
913         free( p_region->psz_text );
914         free( p_region );
915     }
916     return p_dest;
917 }
918
919 /*****************************************************************************
920  * LoadImage: Load an image from file into a picture buffer.
921  *****************************************************************************/
922 static picture_t *LoadImage( intf_thread_t *p_intf, video_format_t *p_fmt,
923                              char *psz_file )
924 {
925     picture_t  *p_pic = NULL;
926
927     if( psz_file && p_intf->p_sys->p_image )
928     {
929         video_format_t fmt_in, fmt_out;
930
931         memset( &fmt_in, 0, sizeof(fmt_in) );
932         memset( &fmt_out, 0, sizeof(fmt_out) );
933
934         fmt_out.i_chroma = p_fmt->i_chroma;
935         p_pic = image_ReadUrl( p_intf->p_sys->p_image, psz_file,
936                                &fmt_in, &fmt_out );
937
938         msg_Dbg( p_intf, "image size %dx%d chroma %4.4s",
939                  fmt_out.i_width, fmt_out.i_height,
940                  (char *)&p_fmt->i_chroma );
941     }
942     return p_pic;
943 }
944
945 #if ! defined(FBOSD_BLENDING)
946 /*****************************************************************************
947  * Convertmage: Convert image to another fourcc
948  *****************************************************************************/
949 static picture_t *ConvertImage( intf_thread_t *p_intf, picture_t *p_pic,
950                          video_format_t *p_fmt_in, video_format_t *p_fmt_out )
951 {
952     intf_sys_t *p_sys = (intf_sys_t *) p_intf->p_sys;
953     picture_t  *p_old = NULL;
954
955     if( p_sys->p_image )
956     {
957         p_old = image_Convert( p_sys->p_image, p_pic, p_fmt_in, p_fmt_out );
958
959         msg_Dbg( p_intf, "converted image size %dx%d chroma %4.4s",
960                  p_fmt_out->i_width, p_fmt_out->i_height,
961                  (char *)&p_fmt_out->i_chroma );
962     }
963     return p_old;
964 }
965 #endif
966
967 /*****************************************************************************
968  * Init: initialize framebuffer video thread output method
969  *****************************************************************************/
970 static int Init( intf_thread_t *p_intf )
971 {
972     intf_sys_t *p_sys = (intf_sys_t *) p_intf->p_sys;
973
974     /* Initialize the output structure: RGB with square pixels, whatever
975      * the input format is, since it's the only format we know */
976     switch( p_sys->var_info.bits_per_pixel )
977     {
978     case 8: /* FIXME: set the palette */
979         p_sys->fmt_out.i_chroma = VLC_FOURCC('R','G','B','2'); break;
980     case 15:
981         p_sys->fmt_out.i_chroma = VLC_FOURCC('R','V','1','5'); break;
982     case 16:
983         p_sys->fmt_out.i_chroma = VLC_FOURCC('R','V','1','6'); break;
984     case 24:
985         p_sys->fmt_out.i_chroma = VLC_FOURCC('R','V','2','4'); break;
986     case 32:
987         p_sys->fmt_out.i_chroma = VLC_FOURCC('R','V','3','2'); break;
988     default:
989         msg_Err( p_intf, "unknown screen depth %i",
990                  p_sys->var_info.bits_per_pixel );
991         return VLC_EGENERIC;
992     }
993
994     p_sys->fmt_out.i_bits_per_pixel = p_sys->var_info.bits_per_pixel;
995     p_sys->fmt_out.i_width  = p_sys->i_width;
996     p_sys->fmt_out.i_height = p_sys->i_height;
997
998     /* Assume we have square pixels */
999     if( p_sys->i_aspect < 0 )
1000     {
1001         p_sys->fmt_out.i_aspect = ( p_sys->i_width
1002                                   * VOUT_ASPECT_FACTOR ) / p_sys->i_height;
1003     }
1004     else p_sys->fmt_out.i_aspect = p_sys->i_aspect;
1005
1006     p_sys->fmt_out.i_sar_num = p_sys->fmt_out.i_sar_den = 1;
1007
1008     /* Allocate overlay buffer */
1009     p_sys->p_overlay = AllocatePicture( VLC_OBJECT(p_intf),
1010                                         &p_sys->fmt_out );
1011     if( !p_sys->p_overlay ) return VLC_EGENERIC;
1012
1013     SetOverlayTransparency( p_intf, true );
1014
1015     /* We know the chroma, allocate a buffer which will be used
1016      * to write to the overlay framebuffer */
1017     p_sys->p_overlay->p->i_pixel_pitch = p_sys->i_bytes_per_pixel;
1018     p_sys->p_overlay->p->i_lines = p_sys->var_info.yres;
1019     p_sys->p_overlay->p->i_visible_lines = p_sys->var_info.yres;
1020
1021     if( p_sys->var_info.xres_virtual )
1022     {
1023         p_sys->p_overlay->p->i_pitch = p_sys->var_info.xres_virtual
1024                              * p_sys->i_bytes_per_pixel;
1025     }
1026     else
1027     {
1028         p_sys->p_overlay->p->i_pitch = p_sys->var_info.xres
1029                              * p_sys->i_bytes_per_pixel;
1030     }
1031
1032     p_sys->p_overlay->p->i_visible_pitch = p_sys->var_info.xres
1033                                  * p_sys->i_bytes_per_pixel;
1034
1035     p_sys->p_overlay->i_planes = 1;
1036
1037     return VLC_SUCCESS;
1038 }
1039
1040 /*****************************************************************************
1041  * End: terminate framebuffer interface
1042  *****************************************************************************/
1043 static void End( intf_thread_t *p_intf )
1044 {
1045     intf_sys_t *p_sys = (intf_sys_t *) p_intf->p_sys;
1046
1047     /* CleanUp */
1048     SetOverlayTransparency( p_intf, false );
1049     if( p_sys->p_overlay )
1050     {
1051         int ret;
1052         ret = write( p_sys->i_fd, p_sys->p_overlay->p[0].p_pixels,
1053                      p_sys->i_page_size );
1054         if( ret < 0 )
1055             msg_Err( p_intf, "unable to clear overlay" );
1056     }
1057
1058     DeAllocatePicture( VLC_OBJECT(p_intf), p_intf->p_sys->p_overlay,
1059                        &p_intf->p_sys->fmt_out );
1060     p_intf->p_sys->p_overlay = NULL;
1061 }
1062
1063 /*****************************************************************************
1064  * OpenDisplay: initialize framebuffer
1065  *****************************************************************************/
1066 static int OpenDisplay( intf_thread_t *p_intf )
1067 {
1068     intf_sys_t *p_sys = (intf_sys_t *) p_intf->p_sys;
1069     char *psz_device;                             /* framebuffer device path */
1070     struct fb_fix_screeninfo    fix_info;     /* framebuffer fix information */
1071
1072     /* Open framebuffer device */
1073     if( !(psz_device = config_GetPsz( p_intf, "fbosd-dev" )) )
1074     {
1075         msg_Err( p_intf, "don't know which fb osd/overlay device to open" );
1076         return VLC_EGENERIC;
1077     }
1078
1079     p_sys->i_fd = open( psz_device, O_RDWR );
1080     if( p_sys->i_fd == -1 )
1081     {
1082         msg_Err( p_intf, "cannot open %s (%s)", psz_device, strerror(errno) );
1083         free( psz_device );
1084         return VLC_EGENERIC;
1085     }
1086     free( psz_device );
1087
1088     /* Get framebuffer device information */
1089     if( ioctl( p_sys->i_fd, FBIOGET_VSCREENINFO, &p_sys->var_info ) )
1090     {
1091         msg_Err( p_intf, "cannot get fb info (%s)", strerror(errno) );
1092         close( p_sys->i_fd );
1093         return VLC_EGENERIC;
1094     }
1095
1096     /* Get some info on the framebuffer itself */
1097     if( ioctl( p_sys->i_fd, FBIOGET_FSCREENINFO, &fix_info ) == 0 )
1098     {
1099         p_sys->i_width = p_sys->fmt_out.i_width = p_sys->var_info.xres;
1100         p_sys->i_height = p_sys->fmt_out.i_height = p_sys->var_info.yres;
1101     }
1102
1103     /* FIXME: if the image is full-size, it gets cropped on the left
1104      * because of the xres / xres_virtual slight difference */
1105     msg_Dbg( p_intf, "%ix%i (virtual %ix%i)",
1106              p_sys->var_info.xres, p_sys->var_info.yres,
1107              p_sys->var_info.xres_virtual,
1108              p_sys->var_info.yres_virtual );
1109
1110     p_sys->fmt_out.i_width = p_sys->i_width;
1111     p_sys->fmt_out.i_height = p_sys->i_height;
1112
1113     p_sys->p_palette = NULL;
1114     p_sys->b_pan = ( fix_info.ypanstep || fix_info.ywrapstep );
1115
1116     switch( p_sys->var_info.bits_per_pixel )
1117     {
1118     case 8:
1119         p_sys->p_palette = malloc( 8 * 256 * sizeof( uint16_t ) );
1120         if( !p_sys->p_palette )
1121         {
1122             close( p_sys->i_fd );
1123             return VLC_ENOMEM;
1124         }
1125         p_sys->fb_cmap.start = 0;
1126         p_sys->fb_cmap.len = 256;
1127         p_sys->fb_cmap.red = p_sys->p_palette;
1128         p_sys->fb_cmap.green  = p_sys->p_palette + 256 * sizeof( uint16_t );
1129         p_sys->fb_cmap.blue   = p_sys->p_palette + 2 * 256 * sizeof( uint16_t );
1130         p_sys->fb_cmap.transp = p_sys->p_palette + 3 * 256 * sizeof( uint16_t );
1131
1132         /* Save the colormap */
1133         ioctl( p_sys->i_fd, FBIOGETCMAP, &p_sys->fb_cmap );
1134
1135         p_sys->i_bytes_per_pixel = 1;
1136         break;
1137
1138     case 15:
1139     case 16:
1140         p_sys->i_bytes_per_pixel = 2;
1141         break;
1142
1143     case 24:
1144         p_sys->i_bytes_per_pixel = 3;
1145         break;
1146
1147     case 32:
1148         p_sys->i_bytes_per_pixel = 4;
1149         break;
1150
1151     default:
1152         msg_Err( p_intf, "screen depth %d is not supported",
1153                          p_sys->var_info.bits_per_pixel );
1154
1155         close( p_sys->i_fd );
1156         return VLC_EGENERIC;
1157     }
1158
1159     p_sys->i_page_size = p_sys->i_width * p_sys->i_height
1160                          * p_sys->i_bytes_per_pixel;
1161
1162     msg_Dbg( p_intf, "framebuffer type=%d, visual=%d, ypanstep=%d, "
1163              "ywrap=%d, accel=%d", fix_info.type, fix_info.visual,
1164              fix_info.ypanstep, fix_info.ywrapstep, fix_info.accel );
1165     return VLC_SUCCESS;
1166 }
1167
1168 /*****************************************************************************
1169  * CloseDisplay: terminate FB interface thread
1170  *****************************************************************************/
1171 static void CloseDisplay( intf_thread_t *p_intf )
1172 {
1173     intf_sys_t *p_sys = (intf_sys_t *) p_intf;
1174
1175     /* Restore palette */
1176     if( p_sys->var_info.bits_per_pixel == 8 )
1177     {
1178         ioctl( p_sys->i_fd, FBIOPUTCMAP, &p_sys->fb_cmap );
1179         free( p_sys->p_palette );
1180         p_sys->p_palette = NULL;
1181     }
1182
1183     /* Close fb */
1184     close( p_sys->i_fd );
1185 }
1186
1187 static void Render( intf_thread_t *p_intf, struct fbosd_render_t *render )
1188 {
1189     intf_sys_t *p_sys = (intf_sys_t*) p_intf->p_sys;
1190
1191     if( render->i_state != FBOSD_STATE_RENDER ) return;
1192     if( !render->psz_string ) return;
1193
1194     if( render->i_type == FBOSD_RENDER_IMAGE )
1195     {
1196         picture_t *p_pic;
1197         p_pic = LoadImage( p_intf, &p_sys->fmt_out, render->psz_string );
1198         if( p_pic )
1199         {
1200             RenderPicture( p_intf, render->i_x, render->i_y,
1201                            p_pic, p_sys->p_overlay );
1202             picture_Release( p_pic );
1203         }
1204     }
1205     else if( render->i_type == FBOSD_RENDER_TEXT )
1206     {
1207         picture_t *p_text;
1208 #if defined(FBOSD_BLENDING)
1209         video_format_t fmt_in;
1210         memset( &fmt_in, 0, sizeof(video_format_t) );
1211         p_text = RenderText( p_intf, render->psz_string, &render->text_style,
1212                              &fmt_in );
1213         if( p_text )
1214         {
1215             BlendPicture( p_intf, &fmt_in, &p_sys->fmt_out,
1216                           p_text, p_sys->p_overlay );
1217             msg_Dbg( p_intf, "releasing picture" );
1218             DeAllocatePicture( VLC_OBJECT( p_intf ), p_text, &fmt_in );
1219         }
1220 #else
1221         p_text = RenderText( p_intf, render->psz_string, &render->text_style,
1222                              &p_sys->fmt_out );
1223         if( p_text )
1224         {
1225             RenderPicture( p_intf, render->i_x, render->i_y,
1226                            p_text, p_sys->p_overlay );
1227             picture_Release( p_text );
1228         }
1229 #endif
1230     }
1231 }
1232
1233 static void RenderClear( intf_thread_t *p_intf, struct fbosd_render_t *render )
1234 {
1235     intf_sys_t *p_sys = (intf_sys_t*) p_intf->p_sys;
1236
1237     vlc_memcpy( &render->text_style, &default_text_style,
1238                 sizeof( text_style_t ) );
1239     free( render->psz_string );
1240     render->psz_string = NULL;
1241
1242     render->i_x = p_sys->i_x;
1243     render->i_y = p_sys->i_y;
1244     render->i_pos = p_sys->i_pos;
1245     render->i_alpha = p_sys->i_alpha;
1246     render->b_absolute = p_sys->b_absolute;
1247     render->i_state = FBOSD_STATE_FREE;
1248 }
1249
1250 static bool isRendererReady( intf_thread_t *p_intf )
1251 {
1252     intf_sys_t *p_sys = (intf_sys_t*) p_intf->p_sys;
1253     int i;
1254
1255     /* Check if there are more items to render */
1256     for( i = 0; i < FBOSD_RENDER_MAX; i++ )
1257     {
1258         if( p_sys->render[i].i_state == FBOSD_STATE_RESERVED )
1259             return false;
1260     }
1261     return true;
1262 }
1263
1264 /*****************************************************************************
1265  * Run: thread
1266  *****************************************************************************
1267  * This part of the interface is in a separate thread so that we can call
1268  * exec() from within it without annoying the rest of the program.
1269  *****************************************************************************/
1270 static void Run( intf_thread_t *p_intf )
1271 {
1272     intf_sys_t *p_sys = (intf_sys_t*) p_intf->p_sys;
1273     int canc = vlc_savecancel();
1274
1275     while( !intf_ShouldDie( p_intf ) )
1276     {
1277         int i;
1278
1279         /* Is there somthing to render? */
1280         for( i = 0; i < FBOSD_RENDER_MAX; i++ )
1281         {
1282             if( p_sys->render[i].i_state == FBOSD_STATE_RENDER )
1283             {
1284                 Render( p_intf, &p_sys->render[i] );
1285                 RenderClear( p_intf, &p_sys->render[i] );
1286             }
1287         }
1288
1289         if( p_sys->b_clear )
1290         {
1291             SetOverlayTransparency( p_intf, true );
1292
1293             var_SetString( p_intf, "fbosd-image", "" );
1294             var_SetString( p_intf, "fbosd-text", "" );
1295
1296             p_sys->b_clear = false;
1297             p_sys->b_need_update = true;
1298         }
1299
1300         if( p_sys->b_need_update && p_sys->p_overlay &&
1301             isRendererReady( p_intf ) )
1302         {
1303             int ret;
1304 #if defined(FBOSD_BLENDING)
1305             /* Reverse alpha channel to work around FPGA bug */
1306             InvertAlpha( p_intf, &p_sys->p_overlay, p_sys->fmt_out );
1307 #endif
1308             ret = write( p_sys->i_fd, p_sys->p_overlay->p[0].p_pixels,
1309                          p_sys->i_page_size );
1310             if( ret < 0 )
1311                 msg_Err( p_intf, "unable to write to overlay" );
1312             lseek( p_sys->i_fd, 0, SEEK_SET );
1313
1314             /* clear the picture */
1315             memset( p_sys->p_overlay->p[0].p_pixels, 0xFF, p_sys->i_page_size );
1316             p_sys->b_need_update = false;
1317         }
1318
1319         if( vlc_CPU() & CPU_CAPABILITY_FPU )
1320             msleep( INTF_IDLE_SLEEP );
1321         else
1322             msleep( 500 );
1323     }
1324
1325     End( p_intf );
1326     vlc_restorecancel( canc );
1327 }
1328
1329 static int OverlayCallback( vlc_object_t *p_this, char const *psz_cmd,
1330                  vlc_value_t oldval, vlc_value_t newval, void *p_data )
1331 {
1332     intf_thread_t *p_intf = (intf_thread_t *) p_this;
1333     intf_sys_t *p_sys = (intf_sys_t*) p_intf->p_sys;
1334     VLC_UNUSED(oldval); VLC_UNUSED(p_data);
1335
1336     if( !strncmp( psz_cmd, "fbosd-display", 13 ) )
1337     {
1338         p_sys->b_need_update = true;
1339     }
1340     else if( !strncmp( psz_cmd, "fbosd-clear", 11 ) )
1341     {
1342         int i;
1343         /* Clear the entire render list */
1344         for( i = 0; i < FBOSD_RENDER_MAX; i++ )
1345         {
1346             RenderClear( p_intf, &p_sys->render[i] );
1347         }
1348         p_sys->b_clear = true;
1349     }
1350     else if( !strncmp( psz_cmd, "fbosd-render", 12 ) )
1351     {
1352         int i;
1353         /* Are we already busy with on slot ? */
1354         for( i = 0; i < FBOSD_RENDER_MAX; i++ )
1355         {
1356             if( p_sys->render[i].i_state == FBOSD_STATE_RESERVED )
1357             {
1358                 p_sys->render[i].i_state = FBOSD_STATE_RENDER;
1359                 break;
1360             }
1361         }
1362     }
1363     else
1364     {
1365         int i;
1366         /* Are we already busy with on slot ? */
1367         for( i = 0; i < FBOSD_RENDER_MAX; i++ )
1368         {
1369             if( p_sys->render[i].i_state == FBOSD_STATE_RESERVED )
1370                 break;
1371         }
1372         /* No, then find first FREE slot */
1373         if( p_sys->render[i].i_state != FBOSD_STATE_RESERVED )
1374         {
1375             for( i = 0; i < FBOSD_RENDER_MAX; i++ )
1376             {
1377                 if( p_sys->render[i].i_state == FBOSD_STATE_FREE )
1378                     break;
1379             }
1380             if( p_sys->render[i].i_state != FBOSD_STATE_FREE )
1381             {
1382                 msg_Warn( p_this, "render space depleated" );
1383                 return VLC_SUCCESS;
1384             }
1385         }
1386         /* Found a free slot */
1387         p_sys->render[i].i_state = FBOSD_STATE_RESERVED;
1388         if( !strncmp( psz_cmd, "fbosd-image", 11 ) )
1389         {
1390             free( p_sys->render[i].psz_string );
1391             p_sys->render[i].psz_string = strdup( newval.psz_string );
1392             p_sys->render[i].i_type = FBOSD_RENDER_IMAGE;
1393         }
1394         else if( !strncmp( psz_cmd, "fbosd-text", 10 ) )
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_TEXT;
1399         }
1400         else if( !strncmp( psz_cmd, "fbosd-x", 7 ) )
1401         {
1402             p_sys->render[i].b_absolute = false;
1403             p_sys->render[i].i_x = (newval.i_int < p_sys->i_width) ?
1404                                     newval.i_int : p_sys->i_width;
1405         }
1406         else if( !strncmp( psz_cmd, "fbosd-y", 7 ) )
1407         {
1408             p_sys->render[i].b_absolute = false;
1409             p_sys->render[i].i_y = (newval.i_int < p_sys->i_height) ?
1410                                     newval.i_int : p_sys->i_height;
1411         }
1412         else if( !strncmp( psz_cmd, "fbosd-position", 14 ) )
1413         {
1414             p_sys->render[i].b_absolute = true;
1415             p_sys->render[i].i_pos = newval.i_int;
1416         }
1417         else if( !strncmp( psz_cmd, "fbosd-font-size", 15 ) )
1418         {
1419             p_sys->render[i].text_style.i_font_size = newval.i_int;
1420         }
1421         else if( !strncmp( psz_cmd, "fbosd-font-color", 16 ) )
1422         {
1423             p_sys->render[i].text_style.i_font_color = newval.i_int;
1424         }
1425         else if( !strncmp( psz_cmd, "fbosd-font-opacity", 18 ) )
1426         {
1427             p_sys->render[i].text_style.i_font_alpha = 255 - newval.i_int;
1428         }
1429         else if( !strncmp( psz_cmd, "fbosd-alpha", 11 ) )
1430         {
1431             p_sys->render[i].i_alpha = newval.i_int;
1432         }
1433     }
1434     return VLC_SUCCESS;
1435 }