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