2 * This file is part of MPlayer.
4 * MPlayer is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * MPlayer is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License along
15 * with MPlayer; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
29 #include "libvo/osd.h"
30 #include "libvo/font_load.h"
31 #include "libvo/sub.h"
32 #include "osdep/keycodes.h"
33 #include "asxparser.h"
34 #include "stream/stream.h"
35 #include "input/input.h"
37 #include "libmpcodecs/img_format.h"
38 #include "libmpcodecs/mp_image.h"
43 extern menu_info_t menu_info_cmdlist
;
44 extern menu_info_t menu_info_chapsel
;
45 extern menu_info_t menu_info_pt
;
46 extern menu_info_t menu_info_filesel
;
47 extern menu_info_t menu_info_txt
;
48 extern menu_info_t menu_info_console
;
49 extern menu_info_t menu_info_pref
;
50 extern menu_info_t menu_info_dvbsel
;
53 menu_info_t
* menu_info_list
[] = {
67 typedef struct key_cmd_s
{
72 typedef struct menu_cmd_bindings_s
{
76 struct menu_cmd_bindings_s
*parent
;
77 } menu_cmd_bindings_t
;
86 double menu_mouse_x
= -1.0;
87 double menu_mouse_y
= -1.0;
88 int menu_mouse_pos_updated
= 0;
90 static struct MPContext
*menu_ctx
= NULL
;
91 static struct m_config
*menu_mconfig
= NULL
;
92 static struct input_ctx
*menu_input
= NULL
;
93 static menu_def_t
* menu_list
= NULL
;
94 static int menu_count
= 0;
95 static menu_cmd_bindings_t
*cmd_bindings
= NULL
;
96 static int cmd_bindings_num
= 0;
99 static menu_cmd_bindings_t
*get_cmd_bindings(const char *name
) {
101 for (i
= 0; i
< cmd_bindings_num
; ++i
)
102 if (!strcasecmp(cmd_bindings
[i
].name
, name
))
103 return &cmd_bindings
[i
];
107 static int menu_parse_config(char* buffer
, struct m_config
*mconfig
)
109 char *element
,*body
, **attribs
, *name
;
110 menu_info_t
* minfo
= NULL
;
112 ASX_Parser_t
* parser
= asx_parser_new(mconfig
);
115 r
= asx_get_element(parser
,&buffer
,&element
,&body
,&attribs
);
117 mp_tmsg(MSGT_GLOBAL
,MSGL_WARN
,"[MENU] syntax error at line: %d\n",parser
->line
);
118 asx_parser_free(parser
);
121 asx_parser_free(parser
);
125 name
= asx_get_attrib("name",attribs
);
127 mp_tmsg(MSGT_GLOBAL
,MSGL_WARN
,"[MENU] Menu definitions need a name attribute (line %d).\n",parser
->line
);
130 asx_free_attribs(attribs
);
134 if (!strcasecmp(element
, "keybindings")) {
135 menu_cmd_bindings_t
*bindings
= cmd_bindings
;
136 char *parent_bindings
;
137 cmd_bindings
= realloc(cmd_bindings
,
138 (cmd_bindings_num
+1)*sizeof(menu_cmd_bindings_t
));
139 for (i
= 0; i
< cmd_bindings_num
; ++i
)
140 if (cmd_bindings
[i
].parent
)
141 cmd_bindings
[i
].parent
= cmd_bindings
[i
].parent
-bindings
+cmd_bindings
;
142 bindings
= &cmd_bindings
[cmd_bindings_num
];
143 memset(bindings
, 0, sizeof(menu_cmd_bindings_t
));
144 bindings
->name
= name
;
145 parent_bindings
= asx_get_attrib("parent",attribs
);
146 if (parent_bindings
) {
147 bindings
->parent
= get_cmd_bindings(parent_bindings
);
148 free(parent_bindings
);
151 asx_free_attribs(attribs
);
157 r
= asx_get_element(parser
,&bd
,&element
,&b
,&attribs
);
159 mp_tmsg(MSGT_GLOBAL
,MSGL_WARN
,"[MENU] syntax error at line: %d\n",
162 asx_parser_free(parser
);
167 if (!strcasecmp(element
, "binding")) {
168 key
= asx_get_attrib("key",attribs
);
169 cmd
= asx_get_attrib("cmd",attribs
);
170 if (key
&& (keycode
= mp_input_get_key_from_name(key
)) >= 0) {
171 keycode
&= ~MP_NO_REPEAT_KEY
;
172 mp_msg(MSGT_GLOBAL
,MSGL_V
,
173 "[libmenu] got keybinding element %d %s=>[%s].\n",
174 keycode
, key
, cmd
? cmd
: "");
175 bindings
->bindings
= realloc(bindings
->bindings
,
176 (bindings
->binding_num
+1)*sizeof(key_cmd_t
));
177 bindings
->bindings
[bindings
->binding_num
].key
= keycode
;
178 bindings
->bindings
[bindings
->binding_num
].cmd
= cmd
;
179 ++bindings
->binding_num
;
186 asx_free_attribs(attribs
);
194 // Try to find this menu type in our list
195 for(i
= 0, minfo
= NULL
; menu_info_list
[i
] ; i
++) {
196 if(strcasecmp(element
,menu_info_list
[i
]->name
) == 0) {
197 minfo
= menu_info_list
[i
];
201 // Got it : add this to our list
203 menu_list
= realloc(menu_list
,(menu_count
+2)*sizeof(menu_def_t
));
204 menu_list
[menu_count
].name
= name
;
205 menu_list
[menu_count
].type
= minfo
;
206 menu_list
[menu_count
].cfg
= m_struct_alloc(&minfo
->priv_st
);
207 menu_list
[menu_count
].args
= body
;
209 for(i
= 0 ; attribs
[2*i
] ; i
++) {
210 if(strcasecmp(attribs
[2*i
],"name") == 0) continue;
211 if(!m_struct_set(&minfo
->priv_st
,menu_list
[menu_count
].cfg
,attribs
[2*i
], attribs
[2*i
+1]))
212 mp_tmsg(MSGT_GLOBAL
,MSGL_WARN
,"[MENU] bad attribute %s=%s in menu '%s' at line %d\n",attribs
[2*i
],attribs
[2*i
+1],
216 memset(&menu_list
[menu_count
],0,sizeof(menu_def_t
));
218 mp_tmsg(MSGT_GLOBAL
,MSGL_WARN
,"[MENU] unknown menu type '%s' at line %d\n",element
,parser
->line
);
224 asx_free_attribs(attribs
);
230 /// This will build the menu_defs list from the cfg file
231 #define BUF_STEP 1024
233 #define BUF_MAX BUF_STEP*1024
234 int menu_init(struct MPContext
*mpctx
, struct m_config
*mconfig
,
235 struct input_ctx
*input_ctx
, char* cfg_file
)
238 int bl
= BUF_STEP
, br
= 0;
240 #ifndef CONFIG_FREETYPE
244 fd
= open(cfg_file
, O_RDONLY
);
246 mp_tmsg(MSGT_GLOBAL
,MSGL_WARN
,"[MENU] Can't open menu config file: %s\n",cfg_file
);
252 if(bl
- br
< BUF_MIN
) {
254 mp_tmsg(MSGT_GLOBAL
,MSGL_WARN
,"[MENU] Config file is too big (> %d KB)\n",BUF_MAX
/1024);
260 buffer
= realloc(buffer
,bl
);
262 r
= read(fd
,buffer
+br
,bl
-br
);
267 mp_tmsg(MSGT_GLOBAL
,MSGL_WARN
,"[MENU] Config file is empty.\n");
275 menu_mconfig
= mconfig
;
276 menu_input
= input_ctx
;
277 f
= menu_parse_config(buffer
, mconfig
);
282 // Destroy all this stuff
283 void menu_uninit(void) {
285 for(i
= 0 ; menu_list
&& menu_list
[i
].name
; i
++) {
286 free(menu_list
[i
].name
);
287 m_struct_free(&menu_list
[i
].type
->priv_st
,menu_list
[i
].cfg
);
288 if(menu_list
[i
].args
) free(menu_list
[i
].args
);
292 for (i
= 0; i
< cmd_bindings_num
; ++i
) {
293 free(cmd_bindings
[i
].name
);
294 while(cmd_bindings
[i
].binding_num
> 0)
295 free(cmd_bindings
[i
].bindings
[--cmd_bindings
[i
].binding_num
].cmd
);
296 free(cmd_bindings
[i
].bindings
);
301 /// Default read_key function
302 int menu_dflt_read_key(menu_t
* menu
,int cmd
) {
304 menu_cmd_bindings_t
*bindings
= get_cmd_bindings(menu
->type
->name
);
306 bindings
= get_cmd_bindings(menu
->type
->type
->name
);
308 bindings
= get_cmd_bindings("default");
310 for (i
= 0; i
< bindings
->binding_num
; ++i
) {
311 if (bindings
->bindings
[i
].key
== cmd
) {
312 if (bindings
->bindings
[i
].cmd
)
313 mp_input_parse_and_queue_cmds(menu
->input_ctx
,
314 bindings
->bindings
[i
].cmd
);
318 bindings
= bindings
->parent
;
323 menu_t
* menu_open(char *name
) {
327 for(i
= 0 ; menu_list
[i
].name
!= NULL
; i
++) {
328 if(strcmp(name
,menu_list
[i
].name
) == 0)
331 if(menu_list
[i
].name
== NULL
) {
332 mp_tmsg(MSGT_GLOBAL
,MSGL_WARN
,"[MENU] Menu %s not found.\n",name
);
335 m
= calloc(1,sizeof(menu_t
));
336 m
->priv_st
= &(menu_list
[i
].type
->priv_st
);
337 m
->priv
= m_struct_copy(m
->priv_st
,menu_list
[i
].cfg
);
339 m
->mconfig
= menu_mconfig
;
340 m
->input_ctx
= menu_input
;
341 m
->type
= &menu_list
[i
];
342 if(menu_list
[i
].type
->open(m
,menu_list
[i
].args
))
345 m_struct_free(m
->priv_st
,m
->priv
);
347 mp_tmsg(MSGT_GLOBAL
,MSGL_WARN
,"[MENU] Menu '%s': Init failed.\n",name
);
351 void menu_draw(menu_t
* menu
,mp_image_t
* mpi
) {
352 if(menu
->show
&& menu
->draw
)
353 menu
->draw(menu
,mpi
);
356 void menu_update_mouse_pos(double x
, double y
) {
359 menu_mouse_pos_updated
= 1;
362 void menu_read_cmd(menu_t
* menu
,int cmd
) {
364 menu
->read_cmd(menu
,cmd
);
367 void menu_close(menu_t
* menu
) {
371 m_struct_free(menu
->priv_st
,menu
->priv
);
375 int menu_read_key(menu_t
* menu
,int cmd
) {
377 return menu
->read_key(menu
,cmd
);
379 return menu_dflt_read_key(menu
,cmd
);
382 ///////////////////////////// Helpers ////////////////////////////////////
384 typedef void (*draw_alpha_f
)(int w
,int h
, unsigned char* src
, unsigned char *srca
, int srcstride
, unsigned char* dstbase
,int dststride
);
386 inline static draw_alpha_f
get_draw_alpha(uint32_t fmt
) {
390 return vo_draw_alpha_rgb15
;
393 return vo_draw_alpha_rgb16
;
396 return vo_draw_alpha_rgb24
;
399 return vo_draw_alpha_rgb32
;
407 return vo_draw_alpha_yv12
;
409 return vo_draw_alpha_yuy2
;
411 return vo_draw_alpha_uyvy
;
417 // return the real height of a char:
418 static inline int get_height(int c
,int h
){
420 if ((font
=vo_font
->font
[c
])>=0)
421 if(h
<vo_font
->pic_a
[font
]->h
) h
=vo_font
->pic_a
[font
]->h
;
425 static void render_txt(char *txt
)
428 int c
= utf8_get_char((const char**)&txt
);
429 render_one_glyph(vo_font
, c
);
433 #ifdef CONFIG_FRIBIDI
434 #include <fribidi/fribidi.h>
435 #include "libavutil/common.h"
436 char *menu_fribidi_charset
= NULL
;
437 int menu_flip_hebrew
= 0;
438 int menu_fribidi_flip_commas
= 0;
440 static char *menu_fribidi(char *txt
)
442 static int char_set_num
= -1;
443 static FriBidiChar
*logical
, *visual
;
444 static size_t buffer_size
= 1024;
445 static char *outputstr
;
447 FriBidiCharType base
;
448 fribidi_boolean log2vis
;
451 if (menu_flip_hebrew
) {
453 if (char_set_num
== -1) {
454 fribidi_set_mirroring (1);
455 fribidi_set_reorder_nsm (0);
456 char_set_num
= fribidi_parse_charset("UTF-8");
457 buffer_size
= FFMAX(1024,len
+1);
458 logical
= malloc(buffer_size
);
459 visual
= malloc(buffer_size
);
460 outputstr
= malloc(buffer_size
);
461 } else if (len
+1 > buffer_size
) {
463 logical
= realloc(logical
, buffer_size
);
464 visual
= realloc(visual
, buffer_size
);
465 outputstr
= realloc(outputstr
, buffer_size
);
467 len
= fribidi_charset_to_unicode (char_set_num
, txt
, len
, logical
);
468 base
= menu_fribidi_flip_commas
?FRIBIDI_TYPE_ON
:FRIBIDI_TYPE_L
;
469 log2vis
= fribidi_log2vis (logical
, len
, &base
, visual
, NULL
, NULL
, NULL
);
471 len
= fribidi_remove_bidi_marks (visual
, len
, NULL
, NULL
, NULL
);
472 fribidi_unicode_to_charset (char_set_num
, visual
, len
, outputstr
);
480 void menu_draw_text(mp_image_t
* mpi
,char* txt
, int x
, int y
) {
481 draw_alpha_f draw_alpha
= get_draw_alpha(mpi
->imgfmt
);
485 mp_tmsg(MSGT_GLOBAL
,MSGL_WARN
,"[MENU] Unsupported output format!!!!\n");
489 #ifdef CONFIG_FRIBIDI
490 txt
= menu_fribidi(txt
);
495 int c
=utf8_get_char((const char**)&txt
);
496 if ((font
=vo_font
->font
[c
])>=0 && (x
+ vo_font
->width
[c
] <= mpi
->w
) && (y
+ vo_font
->pic_a
[font
]->h
<= mpi
->h
))
497 draw_alpha(vo_font
->width
[c
], vo_font
->pic_a
[font
]->h
,
498 vo_font
->pic_b
[font
]->bmp
+vo_font
->start
[c
],
499 vo_font
->pic_a
[font
]->bmp
+vo_font
->start
[c
],
500 vo_font
->pic_a
[font
]->w
,
501 mpi
->planes
[0] + y
* mpi
->stride
[0] + x
* (mpi
->bpp
>>3),
503 x
+=vo_font
->width
[c
]+vo_font
->charspace
;
508 void menu_draw_text_full(mp_image_t
* mpi
,char* txt
,
509 int x
, int y
,int w
, int h
,
510 int vspace
, int warp
, int align
, int anchor
) {
513 int sx
, xmin
, xmax
, xmid
, xrmin
;
516 draw_alpha_f draw_alpha
= get_draw_alpha(mpi
->imgfmt
);
519 mp_tmsg(MSGT_GLOBAL
,MSGL_WARN
,"[MENU] Unsupported output format!!!!\n");
523 #ifdef CONFIG_FRIBIDI
524 txt
= menu_fribidi(txt
);
528 if(x
> mpi
->w
|| y
> mpi
->h
)
531 if(anchor
& MENU_TEXT_VCENTER
) {
532 if(h
<= 0) h
= mpi
->h
;
535 } else if(anchor
& MENU_TEXT_BOT
) {
536 if(h
<= 0) h
= mpi
->h
- y
;
540 if(h
<= 0) h
= mpi
->h
- y
;
545 if(anchor
& MENU_TEXT_HCENTER
) {
546 if(w
<= 0) w
= mpi
->w
;
549 } else if(anchor
& MENU_TEXT_RIGHT
) {
550 if(w
<= 0) w
= mpi
->w
-x
;
554 if(w
<= 0) w
= mpi
->w
-x
;
559 // How many space do we need to draw this ?
560 menu_text_size(txt
,w
,vspace
,warp
,&need_w
,&need_h
);
562 // Find the first line
563 if(align
& MENU_TEXT_VCENTER
)
564 sy
= ymin
+ ((h
- need_h
)/2);
565 else if(align
& MENU_TEXT_BOT
)
566 sy
= ymax
- need_h
- 1;
571 // Find the first col
572 if(align
& MENU_TEXT_HCENTER
)
573 sx
= xmin
+ ((w
- need_w
)/2);
574 else if(align
& MENU_TEXT_RIGHT
)
578 xmid
= xmin
+ (xmax
- xmin
) / 2;
580 // Clamp the bb to the mpi size
581 if(ymin
< 0) ymin
= 0;
582 if(xmin
< 0) xmin
= 0;
583 if(ymax
> mpi
->h
) ymax
= mpi
->h
;
584 if(xmax
> mpi
->w
) xmax
= mpi
->w
;
586 // Jump some the beginnig text if needed
587 while(sy
< ymin
&& *txt
) {
588 int c
=utf8_get_char((const char**)&txt
);
589 if(c
== '\n' || (warp
&& ll
+ vo_font
->width
[c
] > w
)) {
591 sy
+= vo_font
->height
+ vspace
;
592 if(c
== '\n') continue;
594 ll
+= vo_font
->width
[c
]+vo_font
->charspace
;
596 if(*txt
== '\0') // Nothing left to draw
599 while(sy
< ymax
&& *txt
) {
600 char* line_end
= NULL
;
603 if(txt
[0] == '\n') { // New line
604 sy
+= vo_font
->height
+ vspace
;
609 // Get the length and end of this line
610 for(n
= 0, ll
= 0 ; txt
[n
] != '\0' && txt
[n
] != '\n' ; n
++) {
611 unsigned char c
= txt
[n
];
612 if(warp
&& ll
+ vo_font
->width
[c
] > w
) break;
613 ll
+= vo_font
->width
[c
]+vo_font
->charspace
;
616 ll
-= vo_font
->charspace
;
619 if(align
& (MENU_TEXT_HCENTER
|MENU_TEXT_RIGHT
)) {
622 if(align
& MENU_TEXT_HCENTER
) {
624 // Find the middle point
625 for(n
--, ll
= 0 ; n
<= 0 ; n
--) {
626 ll
+= vo_font
->width
[(int)txt
[n
]]+vo_font
->charspace
;
627 if(ll
- vo_font
->charspace
> mid
) break;
629 ll
-= vo_font
->charspace
;
630 sx
= xmid
+ mid
- ll
;
631 } else// MENU_TEXT_RIGHT)
632 sx
= xmax
+ vo_font
->charspace
;
634 // We are after the start point -> go back
636 for(n
-- ; n
<= 0 ; n
--) {
637 unsigned char c
= txt
[n
];
638 if(sx
- vo_font
->width
[c
] - vo_font
->charspace
< xmin
) break;
639 sx
-= vo_font
->width
[c
]+vo_font
->charspace
;
641 } else { // We are before the start point -> go forward
642 for( ; sx
< xmin
&& (&txt
[n
]) != line_end
; n
++) {
643 unsigned char c
= txt
[n
];
644 sx
+= vo_font
->width
[c
]+vo_font
->charspace
;
647 txt
= &txt
[n
]; // Jump to the new start char
649 if(align
& MENU_TEXT_HCENTER
)
655 for(sx
= xrmin
; sx
< xmin
&& txt
!= line_end
; txt
++) {
656 unsigned char c
= txt
[n
];
657 sx
+= vo_font
->width
[c
]+vo_font
->charspace
;
661 while(sx
< xmax
&& txt
!= line_end
) {
662 int c
=utf8_get_char((const char**)&txt
);
663 font
= vo_font
->font
[c
];
665 int cs
= (vo_font
->pic_a
[font
]->h
- vo_font
->height
) / 2;
666 if ((sx
+ vo_font
->width
[c
] <= xmax
) && (sy
+ vo_font
->height
<= ymax
) )
667 draw_alpha(vo_font
->width
[c
], vo_font
->height
,
668 vo_font
->pic_b
[font
]->bmp
+vo_font
->start
[c
] +
669 cs
* vo_font
->pic_a
[font
]->w
,
670 vo_font
->pic_a
[font
]->bmp
+vo_font
->start
[c
] +
671 cs
* vo_font
->pic_a
[font
]->w
,
672 vo_font
->pic_a
[font
]->w
,
673 mpi
->planes
[0] + sy
* mpi
->stride
[0] + sx
* (mpi
->bpp
>>3),
676 //printf("Can't draw '%c'\n",c);
678 sx
+=vo_font
->width
[c
]+vo_font
->charspace
;
681 if(txt
[0] == '\0') break;
682 sy
+= vo_font
->height
+ vspace
;
686 int menu_text_length(char* txt
) {
690 int c
=utf8_get_char((const char**)&txt
);
691 l
+= vo_font
->width
[c
]+vo_font
->charspace
;
693 return l
- vo_font
->charspace
;
696 void menu_text_size(char* txt
,int max_width
, int vspace
, int warp
, int* _w
, int* _h
) {
702 int c
=utf8_get_char((const char**)&txt
);
703 if(c
== '\n' || (warp
&& i
+ vo_font
->width
[c
] >= max_width
)) {
704 i
-= vo_font
->charspace
;
709 if(c
== '\n') continue;
711 i
+= vo_font
->width
[c
]+vo_font
->charspace
;
714 i
-= vo_font
->charspace
;
719 *_h
= (l
-1) * (vo_font
->height
+ vspace
) + vo_font
->height
;
723 int menu_text_num_lines(char* txt
, int max_width
) {
727 int c
=utf8_get_char((const char**)&txt
);
728 if(c
== '\n' || i
+ vo_font
->width
[c
] > max_width
) {
731 if(c
== '\n') continue;
733 i
+= vo_font
->width
[c
]+vo_font
->charspace
;
738 static char* menu_text_get_next_line(char* txt
, int max_width
) {
742 int c
=utf8_get_char((const char**)&txt
);
747 i
+= vo_font
->width
[c
];
750 i
+= vo_font
->charspace
;
756 void menu_draw_box(mp_image_t
* mpi
,unsigned char grey
,unsigned char alpha
, int x
, int y
, int w
, int h
) {
757 draw_alpha_f draw_alpha
= get_draw_alpha(mpi
->imgfmt
);
761 mp_tmsg(MSGT_GLOBAL
,MSGL_WARN
,"[MENU] Unsupported output format!!!!\n");
765 if(x
> mpi
->w
|| y
> mpi
->h
) return;
767 if(x
< 0) w
+= x
, x
= 0;
768 if(x
+w
> mpi
->w
) w
= mpi
->w
-x
;
769 if(y
< 0) h
+= y
, y
= 0;
770 if(y
+h
> mpi
->h
) h
= mpi
->h
-y
;
772 g
= ((256-alpha
)*grey
)>>8;
776 int stride
= (w
+7)&(~7); // round to 8
777 char pic
[stride
*h
],pic_alpha
[stride
*h
];
778 memset(pic
,g
,stride
*h
);
779 memset(pic_alpha
,alpha
,stride
*h
);
780 draw_alpha(w
,h
,pic
,pic_alpha
,stride
,
781 mpi
->planes
[0] + y
* mpi
->stride
[0] + x
* (mpi
->bpp
>>3),