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