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