12 #include "libvo/osd.h"
13 #include "libvo/font_load.h"
14 #include "libvo/sub.h"
15 #include "osdep/keycodes.h"
16 #include "asxparser.h"
17 #include "stream/stream.h"
19 #include "libmpcodecs/img_format.h"
20 #include "libmpcodecs/mp_image.h"
25 extern menu_info_t menu_info_cmdlist
;
26 extern menu_info_t menu_info_pt
;
27 extern menu_info_t menu_info_filesel
;
28 extern menu_info_t menu_info_txt
;
29 extern menu_info_t menu_info_console
;
30 extern menu_info_t menu_info_pref
;
31 #ifdef HAS_DVBIN_SUPPORT
32 extern menu_info_t menu_info_dvbsel
;
36 menu_info_t
* menu_info_list
[] = {
42 #ifdef HAS_DVBIN_SUPPORT
49 typedef struct menu_def_st
{
56 static struct MPContext
*menu_ctx
= NULL
;
57 static menu_def_t
* menu_list
= NULL
;
58 static int menu_count
= 0;
61 static int menu_parse_config(char* buffer
) {
62 char *element
,*body
, **attribs
, *name
;
63 menu_info_t
* minfo
= NULL
;
65 ASX_Parser_t
* parser
= asx_parser_new();
68 r
= asx_get_element(parser
,&buffer
,&element
,&body
,&attribs
);
70 mp_msg(MSGT_GLOBAL
,MSGL_WARN
,MSGTR_LIBMENU_SyntaxErrorAtLine
,parser
->line
);
71 asx_parser_free(parser
);
74 asx_parser_free(parser
);
78 name
= asx_get_attrib("name",attribs
);
80 mp_msg(MSGT_GLOBAL
,MSGL_WARN
,MSGTR_LIBMENU_MenuDefinitionsNeedANameAttrib
,parser
->line
);
83 asx_free_attribs(attribs
);
87 // Try to find this menu type in our list
88 for(i
= 0, minfo
= NULL
; menu_info_list
[i
] ; i
++) {
89 if(strcasecmp(element
,menu_info_list
[i
]->name
) == 0) {
90 minfo
= menu_info_list
[i
];
94 // Got it : add this to our list
96 menu_list
= realloc(menu_list
,(menu_count
+2)*sizeof(menu_def_t
));
97 menu_list
[menu_count
].name
= name
;
98 menu_list
[menu_count
].type
= minfo
;
99 menu_list
[menu_count
].cfg
= m_struct_alloc(&minfo
->priv_st
);
100 menu_list
[menu_count
].args
= body
;
102 for(i
= 0 ; attribs
[2*i
] ; i
++) {
103 if(strcasecmp(attribs
[2*i
],"name") == 0) continue;
104 if(!m_struct_set(&minfo
->priv_st
,menu_list
[menu_count
].cfg
,attribs
[2*i
], attribs
[2*i
+1]))
105 mp_msg(MSGT_GLOBAL
,MSGL_WARN
,MSGTR_LIBMENU_BadAttrib
,attribs
[2*i
],attribs
[2*i
+1],
109 memset(&menu_list
[menu_count
],0,sizeof(menu_def_t
));
111 mp_msg(MSGT_GLOBAL
,MSGL_WARN
,MSGTR_LIBMENU_UnknownMenuType
,element
,parser
->line
);
117 asx_free_attribs(attribs
);
123 /// This will build the menu_defs list from the cfg file
124 #define BUF_STEP 1024
126 #define BUF_MAX BUF_STEP*1024
127 int menu_init(struct MPContext
*mpctx
, char* cfg_file
) {
129 int bl
= BUF_STEP
, br
= 0;
131 #ifndef HAVE_FREETYPE
135 fd
= open(cfg_file
, O_RDONLY
);
137 mp_msg(MSGT_GLOBAL
,MSGL_WARN
,MSGTR_LIBMENU_CantOpenConfigFile
,cfg_file
);
143 if(bl
- br
< BUF_MIN
) {
145 mp_msg(MSGT_GLOBAL
,MSGL_WARN
,MSGTR_LIBMENU_ConfigFileIsTooBig
,BUF_MAX
/1024);
151 buffer
= realloc(buffer
,bl
);
153 r
= read(fd
,buffer
+br
,bl
-br
);
158 mp_msg(MSGT_GLOBAL
,MSGL_WARN
,MSGTR_LIBMENU_ConfigFileIsEmpty
);
166 f
= menu_parse_config(buffer
);
171 // Destroy all this stuff
172 void menu_unint(void) {
174 for(i
= 0 ; menu_list
&& menu_list
[i
].name
; i
++) {
175 free(menu_list
[i
].name
);
176 m_struct_free(&menu_list
[i
].type
->priv_st
,menu_list
[i
].cfg
);
177 if(menu_list
[i
].args
) free(menu_list
[i
].args
);
183 /// Default read_key function
184 void menu_dflt_read_key(menu_t
* menu
,int cmd
) {
187 menu
->read_cmd(menu
,MENU_CMD_UP
);
190 menu
->read_cmd(menu
,MENU_CMD_DOWN
);
193 menu
->read_cmd(menu
,MENU_CMD_LEFT
);
196 menu
->read_cmd(menu
,MENU_CMD_CANCEL
);
199 menu
->read_cmd(menu
,MENU_CMD_RIGHT
);
202 menu
->read_cmd(menu
,MENU_CMD_OK
);
207 menu_t
* menu_open(char *name
) {
211 for(i
= 0 ; menu_list
[i
].name
!= NULL
; i
++) {
212 if(strcmp(name
,menu_list
[i
].name
) == 0)
215 if(menu_list
[i
].name
== NULL
) {
216 mp_msg(MSGT_GLOBAL
,MSGL_WARN
,MSGTR_LIBMENU_MenuNotFound
,name
);
219 m
= calloc(1,sizeof(menu_t
));
220 m
->priv_st
= &(menu_list
[i
].type
->priv_st
);
221 m
->priv
= m_struct_copy(m
->priv_st
,menu_list
[i
].cfg
);
223 if(menu_list
[i
].type
->open(m
,menu_list
[i
].args
))
226 m_struct_free(m
->priv_st
,m
->priv
);
228 mp_msg(MSGT_GLOBAL
,MSGL_WARN
,MSGTR_LIBMENU_MenuInitFailed
,name
);
232 void menu_draw(menu_t
* menu
,mp_image_t
* mpi
) {
233 if(menu
->show
&& menu
->draw
)
234 menu
->draw(menu
,mpi
);
237 void menu_read_cmd(menu_t
* menu
,int cmd
) {
239 menu
->read_cmd(menu
,cmd
);
242 void menu_close(menu_t
* menu
) {
246 m_struct_free(menu
->priv_st
,menu
->priv
);
250 void menu_read_key(menu_t
* menu
,int cmd
) {
252 menu
->read_key(menu
,cmd
);
254 menu_dflt_read_key(menu
,cmd
);
257 ///////////////////////////// Helpers ////////////////////////////////////
259 typedef void (*draw_alpha_f
)(int w
,int h
, unsigned char* src
, unsigned char *srca
, int srcstride
, unsigned char* dstbase
,int dststride
);
261 inline static draw_alpha_f
get_draw_alpha(uint32_t fmt
) {
265 return vo_draw_alpha_rgb15
;
268 return vo_draw_alpha_rgb16
;
271 return vo_draw_alpha_rgb24
;
274 return vo_draw_alpha_rgb32
;
282 return vo_draw_alpha_yv12
;
284 return vo_draw_alpha_yuy2
;
286 return vo_draw_alpha_uyvy
;
292 // return the real height of a char:
293 static inline int get_height(int c
,int h
){
295 if ((font
=vo_font
->font
[c
])>=0)
296 if(h
<vo_font
->pic_a
[font
]->h
) h
=vo_font
->pic_a
[font
]->h
;
300 static void render_txt(char *txt
)
303 int c
= utf8_get_char((const char**)&txt
);
304 render_one_glyph(vo_font
, c
);
309 #include <fribidi/fribidi.h>
310 #include "libavutil/common.h"
311 char *menu_fribidi_charset
= NULL
;
312 int menu_flip_hebrew
= 0;
313 int menu_fribidi_flip_commas
= 0;
315 static char *menu_fribidi(char *txt
)
317 static int char_set_num
= -1;
318 static FriBidiChar
*logical
, *visual
;
319 static size_t buffer_size
= 1024;
320 static char *outputstr
;
322 FriBidiCharType base
;
323 fribidi_boolean log2vis
;
326 if (menu_flip_hebrew
) {
328 if (char_set_num
== -1) {
329 fribidi_set_mirroring (1);
330 fribidi_set_reorder_nsm (0);
331 char_set_num
= fribidi_parse_charset("UTF-8");
332 buffer_size
= FFMAX(1024,len
+1);
333 logical
= malloc(buffer_size
);
334 visual
= malloc(buffer_size
);
335 outputstr
= malloc(buffer_size
);
336 } else if (len
+1 > buffer_size
) {
338 logical
= realloc(logical
, buffer_size
);
339 visual
= realloc(visual
, buffer_size
);
340 outputstr
= realloc(outputstr
, buffer_size
);
342 len
= fribidi_charset_to_unicode (char_set_num
, txt
, len
, logical
);
343 base
= menu_fribidi_flip_commas
?FRIBIDI_TYPE_ON
:FRIBIDI_TYPE_L
;
344 log2vis
= fribidi_log2vis (logical
, len
, &base
, visual
, NULL
, NULL
, NULL
);
346 len
= fribidi_remove_bidi_marks (visual
, len
, NULL
, NULL
, NULL
);
347 fribidi_unicode_to_charset (char_set_num
, visual
, len
, outputstr
);
355 void menu_draw_text(mp_image_t
* mpi
,char* txt
, int x
, int y
) {
356 draw_alpha_f draw_alpha
= get_draw_alpha(mpi
->imgfmt
);
360 mp_msg(MSGT_GLOBAL
,MSGL_WARN
,MSGTR_LIBMENU_UnsupportedOutformat
);
365 txt
= menu_fribidi(txt
);
370 int c
=utf8_get_char((const char**)&txt
);
371 if ((font
=vo_font
->font
[c
])>=0 && (x
+ vo_font
->width
[c
] <= mpi
->w
) && (y
+ vo_font
->pic_a
[font
]->h
<= mpi
->h
))
372 draw_alpha(vo_font
->width
[c
], vo_font
->pic_a
[font
]->h
,
373 vo_font
->pic_b
[font
]->bmp
+vo_font
->start
[c
],
374 vo_font
->pic_a
[font
]->bmp
+vo_font
->start
[c
],
375 vo_font
->pic_a
[font
]->w
,
376 mpi
->planes
[0] + y
* mpi
->stride
[0] + x
* (mpi
->bpp
>>3),
378 x
+=vo_font
->width
[c
]+vo_font
->charspace
;
383 void menu_draw_text_full(mp_image_t
* mpi
,char* txt
,
384 int x
, int y
,int w
, int h
,
385 int vspace
, int warp
, int align
, int anchor
) {
388 int sx
, xmin
, xmax
, xmid
, xrmin
;
391 draw_alpha_f draw_alpha
= get_draw_alpha(mpi
->imgfmt
);
394 mp_msg(MSGT_GLOBAL
,MSGL_WARN
,MSGTR_LIBMENU_UnsupportedOutformat
);
399 txt
= menu_fribidi(txt
);
403 if(x
> mpi
->w
|| y
> mpi
->h
)
406 if(anchor
& MENU_TEXT_VCENTER
) {
407 if(h
<= 0) h
= mpi
->h
;
410 } else if(anchor
& MENU_TEXT_BOT
) {
411 if(h
<= 0) h
= mpi
->h
- y
;
415 if(h
<= 0) h
= mpi
->h
- y
;
420 if(anchor
& MENU_TEXT_HCENTER
) {
421 if(w
<= 0) w
= mpi
->w
;
424 } else if(anchor
& MENU_TEXT_RIGHT
) {
425 if(w
<= 0) w
= mpi
->w
-x
;
429 if(w
<= 0) w
= mpi
->w
-x
;
434 // How many space do we need to draw this ?
435 menu_text_size(txt
,w
,vspace
,warp
,&need_w
,&need_h
);
437 // Find the first line
438 if(align
& MENU_TEXT_VCENTER
)
439 sy
= ymin
+ ((h
- need_h
)/2);
440 else if(align
& MENU_TEXT_BOT
)
441 sy
= ymax
- need_h
- 1;
446 // Find the first col
447 if(align
& MENU_TEXT_HCENTER
)
448 sx
= xmin
+ ((w
- need_w
)/2);
449 else if(align
& MENU_TEXT_RIGHT
)
453 xmid
= xmin
+ (xmax
- xmin
) / 2;
455 // Clamp the bb to the mpi size
456 if(ymin
< 0) ymin
= 0;
457 if(xmin
< 0) xmin
= 0;
458 if(ymax
> mpi
->h
) ymax
= mpi
->h
;
459 if(xmax
> mpi
->w
) xmax
= mpi
->w
;
461 // Jump some the beginnig text if needed
462 while(sy
< ymin
&& *txt
) {
463 int c
=utf8_get_char((const char**)&txt
);
464 if(c
== '\n' || (warp
&& ll
+ vo_font
->width
[c
] > w
)) {
466 sy
+= vo_font
->height
+ vspace
;
467 if(c
== '\n') continue;
469 ll
+= vo_font
->width
[c
]+vo_font
->charspace
;
471 if(*txt
== '\0') // Nothing left to draw
474 while(sy
< ymax
&& *txt
) {
475 char* line_end
= NULL
;
478 if(txt
[0] == '\n') { // New line
479 sy
+= vo_font
->height
+ vspace
;
484 // Get the length and end of this line
485 for(n
= 0, ll
= 0 ; txt
[n
] != '\0' && txt
[n
] != '\n' ; n
++) {
486 unsigned char c
= txt
[n
];
487 if(warp
&& ll
+ vo_font
->width
[c
] > w
) break;
488 ll
+= vo_font
->width
[c
]+vo_font
->charspace
;
491 ll
-= vo_font
->charspace
;
494 if(align
& (MENU_TEXT_HCENTER
|MENU_TEXT_RIGHT
)) {
497 if(align
& MENU_TEXT_HCENTER
) {
499 // Find the middle point
500 for(n
--, ll
= 0 ; n
<= 0 ; n
--) {
501 ll
+= vo_font
->width
[(int)txt
[n
]]+vo_font
->charspace
;
502 if(ll
- vo_font
->charspace
> mid
) break;
504 ll
-= vo_font
->charspace
;
505 sx
= xmid
+ mid
- ll
;
506 } else// MENU_TEXT_RIGHT)
507 sx
= xmax
+ vo_font
->charspace
;
509 // We are after the start point -> go back
511 for(n
-- ; n
<= 0 ; n
--) {
512 unsigned char c
= txt
[n
];
513 if(sx
- vo_font
->width
[c
] - vo_font
->charspace
< xmin
) break;
514 sx
-= vo_font
->width
[c
]+vo_font
->charspace
;
516 } else { // We are before the start point -> go forward
517 for( ; sx
< xmin
&& (&txt
[n
]) != line_end
; n
++) {
518 unsigned char c
= txt
[n
];
519 sx
+= vo_font
->width
[c
]+vo_font
->charspace
;
522 txt
= &txt
[n
]; // Jump to the new start char
524 if(align
& MENU_TEXT_HCENTER
)
530 for(sx
= xrmin
; sx
< xmin
&& txt
!= line_end
; txt
++) {
531 unsigned char c
= txt
[n
];
532 sx
+= vo_font
->width
[c
]+vo_font
->charspace
;
536 while(sx
< xmax
&& txt
!= line_end
) {
537 int c
=utf8_get_char((const char**)&txt
);
538 font
= vo_font
->font
[c
];
540 int cs
= (vo_font
->pic_a
[font
]->h
- vo_font
->height
) / 2;
541 if ((sx
+ vo_font
->width
[c
] < xmax
) && (sy
+ vo_font
->height
< ymax
) )
542 draw_alpha(vo_font
->width
[c
], vo_font
->height
,
543 vo_font
->pic_b
[font
]->bmp
+vo_font
->start
[c
] +
544 cs
* vo_font
->pic_a
[font
]->w
,
545 vo_font
->pic_a
[font
]->bmp
+vo_font
->start
[c
] +
546 cs
* vo_font
->pic_a
[font
]->w
,
547 vo_font
->pic_a
[font
]->w
,
548 mpi
->planes
[0] + sy
* mpi
->stride
[0] + sx
* (mpi
->bpp
>>3),
551 //printf("Can't draw '%c'\n",c);
553 sx
+=vo_font
->width
[c
]+vo_font
->charspace
;
556 if(txt
[0] == '\0') break;
557 sy
+= vo_font
->height
+ vspace
;
561 int menu_text_length(char* txt
) {
565 int c
=utf8_get_char((const char**)&txt
);
566 l
+= vo_font
->width
[c
]+vo_font
->charspace
;
568 return l
- vo_font
->charspace
;
571 void menu_text_size(char* txt
,int max_width
, int vspace
, int warp
, int* _w
, int* _h
) {
577 int c
=utf8_get_char((const char**)&txt
);
578 if(c
== '\n' || (warp
&& i
+ vo_font
->width
[c
] >= max_width
)) {
582 if(c
== '\n') continue;
584 i
+= vo_font
->width
[c
]+vo_font
->charspace
;
589 *_h
= (l
-1) * (vo_font
->height
+ vspace
) + vo_font
->height
;
593 int menu_text_num_lines(char* txt
, int max_width
) {
597 int c
=utf8_get_char((const char**)&txt
);
598 if(c
== '\n' || i
+ vo_font
->width
[c
] > max_width
) {
601 if(c
== '\n') continue;
603 i
+= vo_font
->width
[c
]+vo_font
->charspace
;
608 char* menu_text_get_next_line(char* txt
, int max_width
) {
612 int c
=utf8_get_char((const char**)&txt
);
617 i
+= vo_font
->width
[c
];
620 i
+= vo_font
->charspace
;
626 void menu_draw_box(mp_image_t
* mpi
,unsigned char grey
,unsigned char alpha
, int x
, int y
, int w
, int h
) {
627 draw_alpha_f draw_alpha
= get_draw_alpha(mpi
->imgfmt
);
631 mp_msg(MSGT_GLOBAL
,MSGL_WARN
,MSGTR_LIBMENU_UnsupportedOutformat
);
635 if(x
> mpi
->w
|| y
> mpi
->h
) return;
637 if(x
< 0) w
+= x
, x
= 0;
638 if(x
+w
> mpi
->w
) w
= mpi
->w
-x
;
639 if(y
< 0) h
+= y
, y
= 0;
640 if(y
+h
> mpi
->h
) h
= mpi
->h
-y
;
642 g
= ((256-alpha
)*grey
)>>8;
646 int stride
= (w
+7)&(~7); // round to 8
647 char pic
[stride
*h
],pic_alpha
[stride
*h
];
648 memset(pic
,g
,stride
*h
);
649 memset(pic_alpha
,alpha
,stride
*h
);
650 draw_alpha(w
,h
,pic
,pic_alpha
,stride
,
651 mpi
->planes
[0] + y
* mpi
->stride
[0] + x
* (mpi
->bpp
>>3),