1 /*
2 * This file is part of MPlayer.
3 *
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.
8 *
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.
13 *
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.
17 */
18
19 /**
20 * @file
21 * @brief Skin parser
22 */
23
24 #include <math.h>
25 #include <stdio.h>
26 #include <string.h>
27
28 #include "skin.h"
29 #include "font.h"
30 #include "gui/interface.h"
31 #include "gui/app/app.h"
32 #include "gui/app/gui.h"
33 #include "gui/dialog/dialog.h"
34 #include "gui/util/bitmap.h"
35 #include "gui/util/misc.h"
36 #include "gui/util/string.h"
37
38 #include "help_mp.h"
39 #include "mp_msg.h"
40 #include "libavutil/attributes.h"
41 #include "libavutil/avstring.h"
42 #include "libavutil/common.h"
43
44 typedef struct {
45 const char *name;
46 int (*func)(char *in);
47 } _item;
48
49 char *skinDirInHome;
50 char *skinDirInData;
51
52 static guiItems *skin;
53
54 static int linenumber;
55 static unsigned char currItem[32];
56 static unsigned char path[512];
57
58 static unsigned char currWinName[32];
59 static guiItem *currWin;
60 static int *currWinItemIdx;
61 static guiItem *currWinItems;
62
63 /**
64 * @brief Print a legacy information on an entry.
65 *
66 * @param old identifier (and deprecated entry)
67 * @param data string necessary for checking and to print the information on @a old
68 */
skin_legacy(const char * old,const char * data)69 static void skin_legacy(const char *old, const char *data)
70 {
71 const char *p;
72
73 if (strcmp(old, "fontid") == 0) {
74 p = strchr(data, ',');
75
76 if (p)
77 mp_msg(MSGT_GPLAYER, MSGL_INFO, _(MSGTR_GUI_MSG_SkinLegacy), linenumber, p, "font = fontfile");
78 } else if (strcmp(old, "$l") == 0) {
79 p = strstr(old, data);
80
81 if (p && (p == data || p[-1] != '$'))
82 mp_msg(MSGT_GPLAYER, MSGL_INFO, _(MSGTR_GUI_MSG_SkinLegacy), linenumber, old, "$p");
83 } else if (strcmp(old, "evSetURL") == 0 && strcmp(data, old) == 0)
84 mp_msg(MSGT_GPLAYER, MSGL_INFO, _(MSGTR_GUI_MSG_SkinLegacy), linenumber, old, "evLoadURL");
85 else if (strcmp(old, "sub") == 0 || strcmp(old, "potmeter") == 0)
86 mp_msg(MSGT_GPLAYER, MSGL_INFO, _(MSGTR_GUI_MSG_SkinLegacy), linenumber, old, data);
87 }
88
89 /**
90 * @brief Display a skin error message.
91 *
92 * @param format format string
93 * @param ... arguments
94 */
skin_error(const char * format,...)95 static void skin_error(const char *format, ...)
96 {
97 char p[512];
98 va_list ap;
99
100 va_start(ap, format);
101 vsnprintf(p, sizeof(p), format, ap);
102 va_end(ap);
103
104 gmp_msg(MSGT_GPLAYER, MSGL_ERR, _(MSGTR_GUI_MSG_SkinErrorMessage), linenumber, p);
105 }
106
107 /**
108 * @brief Check whether a @a section definition has started.
109 *
110 * @param item name of the item to be put in a message in case of an error
111 *
112 * @return #True (ok) or #False (error)
113 */
section_item(char * item)114 static int section_item(char *item)
115 {
116 if (!skin) {
117 skin_error(_(MSGTR_GUI_MSG_SkinErrorSection), item);
118 return False;
119 }
120
121 return True;
122 }
123
124 /**
125 * @brief Check whether a @a window definition has started.
126 *
127 * @param item name of the item to be put in a message in case of an error
128 *
129 * @return #True (ok) or #False (error)
130 */
window_item(char * item)131 static int window_item(char *item)
132 {
133 if (!currWinName[0]) {
134 skin_error(_(MSGTR_GUI_MSG_SkinErrorWindow), item);
135 return False;
136 }
137
138 return True;
139 }
140
141 /**
142 * @brief Check whether a specific @a window definition has started.
143 *
144 * @param name name of the window to be checked
145 *
146 * @return 0 (ok) or 1 (error)
147 */
in_window(char * name)148 static int in_window(char *name)
149 {
150 if (strcmp(currWinName, name) == 0) {
151 skin_error(_(MSGTR_GUI_MSG_SkinErrorItem), name);
152 return 1;
153 }
154
155 return 0;
156 }
157
158 /**
159 * @brief Get next free item in current @a window.
160 *
161 * @return pointer to next free item (ok) or NULL (error)
162 */
next_item(void)163 static guiItem *next_item(void)
164 {
165 guiItem *item = NULL;
166
167 if (*currWinItemIdx < MAX_ITEMS - 1) {
168 (*currWinItemIdx)++;
169 item = &currWinItems[*currWinItemIdx];
170 } else
171 skin_error(_(MSGTR_GUI_MSG_SkinTooManyItems));
172
173 return item;
174 }
175
176 /**
177 * @brief Parse a @a section definition.
178 *
179 * Syntax: section=movieplayer
180 *
181 * @param in definition to be analyzed
182 *
183 * @return 0 (ok) or 1 (error)
184 */
item_section(char * in)185 static int item_section(char *in)
186 {
187 if (skin) {
188 skin_error(_(MSGTR_GUI_MSG_SkinErrorItem), currItem);
189 return 1;
190 }
191
192 if (strcmp(strlower(in), "movieplayer") == 0)
193 skin = &guiApp;
194 else {
195 skin_error(_(MSGTR_GUI_MSG_SkinUnknownName), in);
196 return 1;
197 }
198
199 mp_msg(MSGT_GPLAYER, MSGL_DBG2, "[skin] %s: %s\n", currItem, in);
200
201 return 0;
202 }
203
204 /**
205 * @brief Parse an @a end definition.
206 *
207 * Syntax: end
208 *
209 * @param in definition to be analyzed
210 *
211 * @return 0 (ok) or 1 (error)
212 */
item_end(char * in)213 static int item_end(char *in)
214 {
215 char *space, *name;
216
217 (void)in;
218
219 if (currWinName[0]) {
220 space = " ";
221 name = currWinName;
222 } else {
223 space = "";
224 name = "section";
225 }
226
227 if (!section_item(currItem))
228 return 1;
229
230 mp_msg(MSGT_GPLAYER, MSGL_DBG2, "[skin] %s%s (%s)\n", space, currItem, name);
231
232 if (currWinName[0]) {
233 currWinName[0] = 0;
234 currWin = NULL;
235 currWinItemIdx = NULL;
236 currWinItems = NULL;
237 } else
238 skin = NULL;
239
240 return 0;
241 }
242
243 /**
244 * @brief Parse a @a window definition.
245 *
246 * Syntax: window=main|video|playbar|menu
247 *
248 * @param in definition to be analyzed
249 *
250 * @return 0 (ok) or 1 (error)
251 */
item_window(char * in)252 static int item_window(char *in)
253 {
254 if (!section_item(currItem))
255 return 1;
256
257 if (currWinName[0]) {
258 skin_error(_(MSGTR_GUI_MSG_SkinErrorItem), currItem);
259 return 1;
260 }
261
262 strlower(in);
263
264 // legacy
265 if (strcmp(in, "sub") == 0) {
266 strcpy(in, "video");
267 skin_legacy("sub", in);
268 }
269
270 if (strcmp(in, "main") == 0) {
271 currWin = &skin->main;
272 currWinItemIdx = &skin->IndexOfMainItems;
273 currWinItems = skin->mainItems;
274 } else if (strcmp(in, "video") == 0) {
275 currWin = &skin->video;
276 currWinItemIdx = NULL;
277 currWinItems = NULL;
278 } else if (strcmp(in, "playbar") == 0) {
279 currWin = &skin->playbar;
280 currWinItemIdx = &skin->IndexOfPlaybarItems;
281 currWinItems = skin->playbarItems;
282 } else if (strcmp(in, "menu") == 0) {
283 currWin = &skin->menu;
284 currWinItemIdx = &skin->IndexOfMenuItems;
285 currWinItems = skin->menuItems;
286 } else {
287 skin_error(_(MSGTR_GUI_MSG_SkinUnknownName), in);
288 return 1;
289 }
290
291 av_strlcpy(currWinName, in, sizeof(currWinName));
292
293 mp_msg(MSGT_GPLAYER, MSGL_DBG2, "[skin] %s: %s\n", currItem, currWinName);
294
295 return 0;
296 }
297
298 /**
299 * @brief Parse a @a base definition.
300 *
301 * Syntax: base=image,x,y[,width,height]
302 *
303 * @param in definition to be analyzed
304 *
305 * @return 0 (ok) or 1 (error)
306 */
item_base(char * in)307 static int item_base(char *in)
308 {
309 unsigned char fname[256];
310 unsigned char file[512];
311 int x, y, w, h;
312 int is_video, is_bar, is_menu;
313
314 if (!window_item(currItem))
315 return 1;
316
317 is_video = (strcmp(currWinName, "video") == 0);
318 is_bar = (strcmp(currWinName, "playbar") == 0);
319 is_menu = (strcmp(currWinName, "menu") == 0);
320
321 cutStr(in, fname, ',', 0);
322 x = cutInt(in, ',', 1);
323 y = cutInt(in, ',', 2);
324 w = cutInt(in, ',', 3);
325 h = cutInt(in, ',', 4);
326
327 mp_msg(MSGT_GPLAYER, MSGL_DBG2, "[skin] image: %s", fname);
328
329 currWin->type = itBase;
330
331 if (!is_menu) {
332 currWin->x = x;
333 currWin->y = y;
334
335 mp_msg(MSGT_GPLAYER, MSGL_DBG2, " %d,%d", x, y);
336 }
337
338 mp_msg(MSGT_GPLAYER, MSGL_DBG2, "\n");
339
340 if (is_video && (strcmp(fname, "NULL") == 0)) {
341 currWin->width = 0;
342 currWin->height = 0;
343 } else {
344 av_strlcpy(file, path, sizeof(file));
345 av_strlcat(file, fname, sizeof(file));
346
347 if (skinImageRead(file, &currWin->Bitmap) != 0)
348 return 1;
349
350 currWin->width = currWin->Bitmap.Width;
351 currWin->height = currWin->Bitmap.Height;
352 }
353
354 if (is_video) {
355 if (w && h) {
356 currWin->width = w;
357 currWin->height = h;
358 }
359 }
360
361 if (currWin->width == 0 || currWin->height == 0)
362 return 1;
363
364 mp_msg(MSGT_GPLAYER, MSGL_DBG2, "[skin] %s: %dx%d\n", is_video && w && h ? "size" : " bitmap", currWin->width, currWin->height);
365
366 if (!is_video) {
367 if (!bpRenderMask(&currWin->Bitmap, &currWin->Mask)) {
368 skin_error(_(MSGTR_GUI_MSG_SkinMemoryError));
369 return 1;
370 }
371 mp_msg(MSGT_GPLAYER, MSGL_DBG2, "[skin] mask: %ux%u\n", currWin->Mask.Width, currWin->Mask.Height);
372 }
373
374 if (is_bar)
375 skin->playbarIsPresent = True;
376 if (is_menu)
377 skin->menuIsPresent = True;
378
379 return 0;
380 }
381
382 /**
383 * @brief Parse a @a background definition.
384 *
385 * Syntax: background=R,G,B
386 *
387 * @param in definition to be analyzed
388 *
389 * @return 0 (ok) or 1 (error)
390 */
item_background(char * in)391 static int item_background(char *in)
392 {
393 if (!window_item(currItem))
394 return 1;
395
396 if (in_window("main"))
397 return 1;
398 if (in_window("playbar"))
399 return 1;
400 if (in_window("menu"))
401 return 1;
402
403 currWin->R = cutInt(in, ',', 0);
404 currWin->G = cutInt(in, ',', 1);
405 currWin->B = cutInt(in, ',', 2);
406
407 mp_msg(MSGT_GPLAYER, MSGL_DBG2, "[skin] %s color: #%02x%02x%02x\n", currItem, currWin->R, currWin->G, currWin->B);
408
409 return 0;
410 }
411
412 /**
413 * @brief Parse a @a button definition.
414 *
415 * Syntax: button=image,x,y,width,height,message
416 *
417 * @param in definition to be analyzed
418 *
419 * @return 0 (ok) or 1 (error)
420 */
item_button(char * in)421 static int item_button(char *in)
422 {
423 unsigned char fname[256];
424 unsigned char file[512];
425 int x, y, w, h, message;
426 char msg[32];
427 guiItem *item;
428
429 if (!window_item(currItem))
430 return 1;
431
432 if (in_window("video"))
433 return 1;
434 if (in_window("menu"))
435 return 1;
436
437 cutStr(in, fname, ',', 0);
438 x = cutInt(in, ',', 1);
439 y = cutInt(in, ',', 2);
440 w = cutInt(in, ',', 3);
441 h = cutInt(in, ',', 4);
442 cutStr(in, msg, ',', 5);
443
444 message = appFindMessage(msg);
445
446 if (message == -1) {
447 skin_error(_(MSGTR_GUI_MSG_SkinUnknownMessage), msg);
448 return 1;
449 }
450 // legacy
451 else
452 skin_legacy("evSetURL", msg);
453
454 mp_msg(MSGT_GPLAYER, MSGL_DBG2, "[skin] %s image: %s %d,%d\n", currItem, fname, x, y);
455 mp_msg(MSGT_GPLAYER, MSGL_DBG2, "[skin] message: %s (#%d)\n", msg, message);
456 mp_msg(MSGT_GPLAYER, MSGL_DBG2, "[skin] size: %dx%d\n", w, h);
457
458 item = next_item();
459
460 if (!item)
461 return 1;
462
463 item->type = itButton;
464 item->x = x;
465 item->y = y;
466 item->width = w;
467 item->height = h;
468 item->message = message;
469 item->pressed = btnReleased;
470
471 if (item->message == evPauseSwitchToPlay)
472 item->pressed = btnDisabled;
473
474 item->Bitmap.Image = NULL;
475
476 if (strcmp(fname, "NULL") != 0) {
477 av_strlcpy(file, path, sizeof(file));
478 av_strlcat(file, fname, sizeof(file));
479
480 if (skinImageRead(file, &item->Bitmap) != 0)
481 return 1;
482
483 mp_msg(MSGT_GPLAYER, MSGL_DBG2, "[skin] (bitmap: %ux%u)\n", item->Bitmap.Width, item->Bitmap.Height);
484 }
485
486 return 0;
487 }
488
489 /**
490 * @brief Parse a @a selected definition.
491 *
492 * Syntax: selected=image
493 *
494 * @param in definition to be analyzed
495 *
496 * @return 0 (ok) or 1 (error)
497 */
item_selected(char * in)498 static int item_selected(char *in)
499 {
500 unsigned char file[512];
501 guiItem *item;
502
503 if (!window_item(currItem))
504 return 1;
505
506 if (in_window("main"))
507 return 1;
508 if (in_window("video"))
509 return 1;
510 if (in_window("playbar"))
511 return 1;
512
513 mp_msg(MSGT_GPLAYER, MSGL_DBG2, "[skin] image %s: %s\n", currItem, in);
514
515 item = &skin->menuSelected;
516 item->type = itBase;
517
518 av_strlcpy(file, path, sizeof(file));
519 av_strlcat(file, in, sizeof(file));
520
521 if (skinImageRead(file, &item->Bitmap) != 0)
522 return 1;
523
524 item->width = item->Bitmap.Width;
525 item->height = item->Bitmap.Height;
526
527 mp_msg(MSGT_GPLAYER, MSGL_DBG2, "[skin] bitmap: %dx%d\n", item->width, item->height);
528
529 return 0;
530 }
531
532 /**
533 * @brief Parse a @a menu definition.
534 *
535 * Syntax: menu=x,y,width,height,message
536 *
537 * @param in definition to be analyzed
538 *
539 * @return 0 (ok) or 1 (error)
540 */
item_menu(char * in)541 static int item_menu(char *in)
542 {
543 int x, y, w, h, message;
544 char msg[32];
545 guiItem *item;
546
547 if (!window_item(currItem))
548 return 1;
549
550 if (in_window("main"))
551 return 1;
552 if (in_window("video"))
553 return 1;
554 if (in_window("playbar"))
555 return 1;
556
557 x = cutInt(in, ',', 0);
558 y = cutInt(in, ',', 1);
559 w = cutInt(in, ',', 2);
560 h = cutInt(in, ',', 3);
561 cutStr(in, msg, ',', 4);
562
563 message = appFindMessage(msg);
564
565 if (message == -1) {
566 skin_error(_(MSGTR_GUI_MSG_SkinUnknownMessage), msg);
567 return 1;
568 }
569 // legacy
570 else
571 skin_legacy("evSetURL", msg);
572
573 item = next_item();
574
575 if (!item)
576 return 1;
577
578 item->type = itMenu;
579 item->x = x;
580 item->y = y;
581 item->width = w;
582 item->height = h;
583 item->message = message;
584
585 mp_msg(MSGT_GPLAYER, MSGL_DBG2, "[skin] item #%d: %d,%d %dx%d\n", *currWinItemIdx, x, y, w, h);
586 mp_msg(MSGT_GPLAYER, MSGL_DBG2, "[skin] message: %s (#%d)\n", msg, message);
587
588 return 0;
589 }
590
591 /**
592 * @brief Parse a hpotmeter, vpotmeter or rpotmeter definition.
593 *
594 * Parameters: button,bwidth,bheight,phases,numphases,[x0,y0,x1,y1,]default,x,y,width,height,message
595 *
596 * @param item memory location of an item to store the parameters in
597 * @param in definition to be analyzed
598 *
599 * @note item->type is already available.
600 *
601 * @return 0 (ok) or 1 (error)
602 */
parse_potmeter(guiItem * item,char * in)603 static int parse_potmeter(guiItem *item, char *in)
604 {
605 unsigned char bfname[256];
606 unsigned char phfname[256];
607 unsigned char buf[512], dfmt[5];
608 int i = 0, av_uninit(x0), av_uninit(y0), av_uninit(x1), av_uninit(y1);
609 int bwidth, bheight, num, no_default, d, x, y, w, h, message;
610
611 if (!window_item(currItem))
612 return 1;
613
614 if (in_window("video"))
615 return 1;
616 if (in_window("menu"))
617 return 1;
618
619 cutStr(in, bfname, ',', i++);
620 bwidth = cutInt(in, ',', i++);
621 bheight = cutInt(in, ',', i++);
622 cutStr(in, phfname, ',', i++);
623 num = cutInt(in, ',', i++);
624
625 if (item->type == itRPotmeter) {
626 x0 = cutInt(in, ',', i++);
627 y0 = cutInt(in, ',', i++);
628 x1 = cutInt(in, ',', i++);
629 y1 = cutInt(in, ',', i++);
630 }
631
632 cutStr(in, buf, ',', i);
633 no_default = (strcmp(buf, "-") == 0);
634
635 d = cutInt(in, ',', i++);
636 x = cutInt(in, ',', i++);
637 y = cutInt(in, ',', i++);
638 w = cutInt(in, ',', i++);
639 h = cutInt(in, ',', i++);
640 cutStr(in, buf, ',', i++);
641
642 message = appFindMessage(buf);
643
644 if (message == -1) {
645 skin_error(_(MSGTR_GUI_MSG_SkinUnknownMessage), buf);
646 return 1;
647 }
648 // legacy
649 else
650 skin_legacy("evSetURL", buf);
651
652 if (d < 0 || d > 100) {
653 skin_error(_(MSGTR_GUI_MSG_SkinErrorDefault), d);
654 return 1;
655 }
656
657 if ((message == evSetVolume) && no_default) {
658 d = -1;
659 strcpy(dfmt, "-");
660 } else
661 sprintf(dfmt, "%d%%", d);
662
663 mp_msg(MSGT_GPLAYER, MSGL_DBG2, "[skin] %s image: %s %d,%d %dx%d\n", currItem, phfname, x, y, w, h);
664 mp_msg(MSGT_GPLAYER, MSGL_DBG2, "[skin] button image: %s %dx%d\n", bfname, bwidth, bheight);
665 mp_msg(MSGT_GPLAYER, MSGL_DBG2, "[skin] numphases: %d, default: %s\n", num, dfmt);
666 mp_msg(MSGT_GPLAYER, MSGL_DBG2, "[skin] message: %s (#%d)\n", buf, message);
667
668 item->x = x;
669 item->y = y;
670 item->width = w;
671 item->height = h;
672 item->pbwidth = bwidth;
673 item->pbheight = bheight;
674 item->numphases = num;
675 item->value = (float)d;
676 item->message = message;
677 item->pressed = btnReleased;
678
679 if (item->type == itRPotmeter) {
680 mp_msg(MSGT_GPLAYER, MSGL_DBG2, "[skin] start: %d,%d / stop: %d,%d\n", x0, y0, x1, y1);
681
682 item->zeropoint = appRadian(item, x0, y0);
683 item->arclength = appRadian(item, x1, y1) - item->zeropoint;
684
685 if (item->arclength < 0.0)
686 item->arclength += 2 * M_PI;
687 // else check if radians of (x0,y0) and (x1,y1) only differ below threshold
688 else if (item->arclength < 0.05)
689 item->arclength = 2 * M_PI;
690 }
691
692 item->Bitmap.Image = NULL;
693
694 if (strcmp(phfname, "NULL") != 0) {
695 if (num == 0) {
696 skin_error(_(MSGTR_GUI_MSG_SkinErrorNumphases));
697 return 1;
698 }
699
700 av_strlcpy(buf, path, sizeof(buf));
701 av_strlcat(buf, phfname, sizeof(buf));
702
703 if (skinImageRead(buf, &item->Bitmap) != 0)
704 return 1;
705
706 mp_msg(MSGT_GPLAYER, MSGL_DBG2, "[skin] (%s bitmap: %ux%u)\n", currItem, item->Bitmap.Width, item->Bitmap.Height);
707 }
708
709 item->Mask.Image = NULL;
710
711 if (strcmp(bfname, "NULL") != 0) {
712 av_strlcpy(buf, path, sizeof(buf));
713 av_strlcat(buf, bfname, sizeof(buf));
714
715 if (skinImageRead(buf, &item->Mask) != 0)
716 return 1;
717
718 mp_msg(MSGT_GPLAYER, MSGL_DBG2, "[skin] (button bitmap: %ux%u)\n", item->Mask.Width, item->Mask.Height);
719 }
720
721 return 0;
722 }
723
724 /**
725 * @brief Parse a @a hpotmeter definition.
726 *
727 * Syntax: hpotmeter=button,bwidth,bheight,phases,numphases,default,x,y,width,height,message
728 *
729 * @param in definition to be analyzed
730 *
731 * @return 0 (ok) or 1 (error)
732 */
item_hpotmeter(char * in)733 static int item_hpotmeter(char *in)
734 {
735 guiItem *item;
736
737 item = next_item();
738
739 if (!item)
740 return 1;
741
742 item->type = itHPotmeter;
743
744 return parse_potmeter(item, in);
745 }
746
747 /**
748 * @brief Parse a @a vpotmeter definition.
749 *
750 * Syntax: vpotmeter=button,bwidth,bheight,phases,numphases,default,x,y,width,height,message
751 *
752 * @param in definition to be analyzed
753 *
754 * @return 0 (ok) or 1 (error)
755 */
item_vpotmeter(char * in)756 static int item_vpotmeter(char *in)
757 {
758 guiItem *item;
759
760 item = next_item();
761
762 if (!item)
763 return 1;
764
765 item->type = itVPotmeter;
766
767 return parse_potmeter(item, in);
768 }
769
770 /**
771 * @brief Parse a @a rpotmeter definition.
772 *
773 * Syntax: rpotmeter=button,bwidth,bheight,phases,numphases,x0,y0,x1,y1,default,x,y,width,height,message
774 *
775 * @param in definition to be analyzed
776 *
777 * @return 0 (ok) or 1 (error)
778 */
item_rpotmeter(char * in)779 static int item_rpotmeter(char *in)
780 {
781 guiItem *item;
782
783 item = next_item();
784
785 if (!item)
786 return 1;
787
788 item->type = itRPotmeter;
789
790 return parse_potmeter(item, in);
791 }
792
793 /**
794 * @brief Parse a @a potmeter definition.
795 *
796 * Syntax: potmeter=phases,numphases,default,x,y,width,height,message
797 *
798 * @note THIS ITEM IS DEPRECATED.
799 *
800 * @param in definition to be analyzed
801 *
802 * @return 0 (ok) or 1 (error)
803 */
item_potmeter(char * in)804 static int item_potmeter(char *in)
805 {
806 char param[256];
807
808 // legacy
809 skin_legacy(currItem, "hpotmeter");
810
811 snprintf(param, sizeof(param), "NULL,0,0,%s", in);
812
813 return item_hpotmeter(param);
814 }
815
816 /**
817 * @brief Parse a @a pimage definition.
818 *
819 * Syntax: pimage=phases,numphases,default,x,y,width,height,message
820 *
821 * @param in definition to be analyzed
822 *
823 * @return 0 (ok) or 1 (error)
824 */
item_pimage(char * in)825 static int item_pimage(char *in)
826 {
827 unsigned char phfname[256];
828 unsigned char buf[512];
829 int num, d, x, y, w, h, message;
830 guiItem *item;
831
832 if (!window_item(currItem))
833 return 1;
834
835 if (in_window("video"))
836 return 1;
837 if (in_window("menu"))
838 return 1;
839
840 cutStr(in, phfname, ',', 0);
841 num = cutInt(in, ',', 1);
842 d = cutInt(in, ',', 2);
843 x = cutInt(in, ',', 3);
844 y = cutInt(in, ',', 4);
845 w = cutInt(in, ',', 5);
846 h = cutInt(in, ',', 6);
847 cutStr(in, buf, ',', 7);
848
849 message = appFindMessage(buf);
850
851 if (message == -1) {
852 skin_error(_(MSGTR_GUI_MSG_SkinUnknownMessage), buf);
853 return 1;
854 }
855 // legacy
856 else
857 skin_legacy("evSetURL", buf);
858
859 if (d < 0 || d > 100) {
860 skin_error(_(MSGTR_GUI_MSG_SkinErrorDefault), d);
861 return 1;
862 }
863
864 mp_msg(MSGT_GPLAYER, MSGL_DBG2, "[skin] %s image: %s %d,%d %dx%d\n", currItem, phfname, x, y, w, h);
865 mp_msg(MSGT_GPLAYER, MSGL_DBG2, "[skin] numphases: %d, default: %d%%\n", num, d);
866 mp_msg(MSGT_GPLAYER, MSGL_DBG2, "[skin] message: %s (#%d)\n", buf, message);
867
868 item = next_item();
869
870 if (!item)
871 return 1;
872
873 item->type = itPimage;
874 item->x = x;
875 item->y = y;
876 item->width = w;
877 item->height = h;
878 item->numphases = num;
879 item->value = (float)d;
880 item->message = message;
881
882 item->Bitmap.Image = NULL;
883
884 if (strcmp(phfname, "NULL") != 0) {
885 if (num == 0) {
886 skin_error(_(MSGTR_GUI_MSG_SkinErrorNumphases));
887 return 1;
888 }
889
890 av_strlcpy(buf, path, sizeof(buf));
891 av_strlcat(buf, phfname, sizeof(buf));
892
893 if (skinImageRead(buf, &item->Bitmap) != 0)
894 return 1;
895
896 mp_msg(MSGT_GPLAYER, MSGL_DBG2, "[skin] (bitmap: %ux%u)\n", item->Bitmap.Width, item->Bitmap.Height);
897 }
898
899 return 0;
900 }
901
902 /**
903 * @brief Parse a @a font definition.
904 *
905 * Syntax: font=fontfile
906 *
907 * @param in definition to be analyzed
908 *
909 * @return 0 (ok) or 1 (error)
910 */
item_font(char * in)911 static int item_font(char *in)
912 {
913 char fnt[256];
914
915 if (!window_item(currItem))
916 return 1;
917
918 if (in_window("video"))
919 return 1;
920 if (in_window("menu"))
921 return 1;
922
923 cutStr(in, fnt, ',', 0); // Note: This seems needless but isn't for compatibility
924 // reasons with a meanwhile deprecated second parameter.
925 // legacy
926 skin_legacy("fontid", in);
927
928 switch (fntRead(path, fnt)) {
929 case -1:
930 skin_error(_(MSGTR_GUI_MSG_SkinMemoryError));
931 return 1;
932
933 case -2:
934 skin_error(_(MSGTR_GUI_MSG_SkinTooManyFonts));
935 return 1;
936
937 case -3:
938 skin_error(_(MSGTR_GUI_MSG_SkinFontFileNotFound));
939 return 1;
940
941 case -4:
942 skin_error(_(MSGTR_GUI_MSG_SkinFontImageNotFound));
943 return 1;
944 }
945
946 mp_msg(MSGT_GPLAYER, MSGL_DBG2, "[skin] %s: %s (#%d)\n", currItem, fnt, fntFindID(fnt));
947
948 return 0;
949 }
950
951 /**
952 * @brief Parse a @a slabel definition.
953 *
954 * Syntax: slabel=x,y,fontfile,"text"
955 *
956 * @param in definition to be analyzed
957 *
958 * @return 0 (ok) or 1 (error)
959 */
item_slabel(char * in)960 static int item_slabel(char *in)
961 {
962 int x, y, id;
963 char fnt[256];
964 char txt[256];
965 guiItem *item;
966
967 if (!window_item(currItem))
968 return 1;
969
970 if (in_window("video"))
971 return 1;
972 if (in_window("menu"))
973 return 1;
974
975 x = cutInt(in, ',', 0);
976 y = cutInt(in, ',', 1);
977 cutStr(in, fnt, ',', 2);
978 cutStr(in, txt, ',', 3);
979 cutStr(txt, txt, '"', 1);
980
981 mp_msg(MSGT_GPLAYER, MSGL_DBG2, "[skin] %s: \"%s\"\n", currItem, txt);
982 mp_msg(MSGT_GPLAYER, MSGL_DBG2, "[skin] pos: %d,%d\n", x, y);
983
984 id = fntFindID(fnt);
985
986 if (id < 0) {
987 skin_error(_(MSGTR_GUI_MSG_SkinFontNotFound), fnt);
988 return 1;
989 }
990
991 mp_msg(MSGT_GPLAYER, MSGL_DBG2, "[skin] font: %s (#%d)\n", fnt, id);
992
993 item = next_item();
994
995 if (!item)
996 return 1;
997
998 item->type = itSLabel;
999 item->x = x;
1000 item->y = y;
1001 item->width = -1;
1002 item->height = -1;
1003 item->fontid = id;
1004 item->label = strdup(txt);
1005
1006 if (!item->label) {
1007 skin_error(_(MSGTR_GUI_MSG_SkinMemoryError));
1008 return 1;
1009 }
1010
1011 return 0;
1012 }
1013
1014 /**
1015 * @brief Parse a @a dlabel definition.
1016 *
1017 * Syntax: dlabel=x,y,width,align,fontfile,"text"
1018 *
1019 * @param in definition to be analyzed
1020 *
1021 * @return 0 (ok) or 1 (error)
1022 */
item_dlabel(char * in)1023 static int item_dlabel(char *in)
1024 {
1025 int x, y, w, a, id;
1026 char fnt[256];
1027 char txt[256];
1028 guiItem *item;
1029
1030 if (!window_item(currItem))
1031 return 1;
1032
1033 if (in_window("video"))
1034 return 1;
1035 if (in_window("menu"))
1036 return 1;
1037
1038 x = cutInt(in, ',', 0);
1039 y = cutInt(in, ',', 1);
1040 w = cutInt(in, ',', 2);
1041 a = cutInt(in, ',', 3);
1042 cutStr(in, fnt, ',', 4);
1043 cutStr(in, txt, ',', 5);
1044 cutStr(txt, txt, '"', 1);
1045
1046 // legacy
1047 skin_legacy("$l", txt);
1048
1049 mp_msg(MSGT_GPLAYER, MSGL_DBG2, "[skin] %s: \"%s\"\n", currItem, txt);
1050 mp_msg(MSGT_GPLAYER, MSGL_DBG2, "[skin] pos: %d,%d\n", x, y);
1051 mp_msg(MSGT_GPLAYER, MSGL_DBG2, "[skin] width: %d, align: %d\n", w, a);
1052
1053 id = fntFindID(fnt);
1054
1055 if (id < 0) {
1056 skin_error(_(MSGTR_GUI_MSG_SkinFontNotFound), fnt);
1057 return 1;
1058 }
1059
1060 mp_msg(MSGT_GPLAYER, MSGL_DBG2, "[skin] font: %s (#%d)\n", fnt, id);
1061
1062 item = next_item();
1063
1064 if (!item)
1065 return 1;
1066
1067 item->type = itDLabel;
1068 item->x = x;
1069 item->y = y;
1070 item->width = w;
1071 item->height = -1;
1072 item->fontid = id;
1073 item->align = a;
1074 item->label = strdup(txt);
1075
1076 if (!item->label) {
1077 skin_error(_(MSGTR_GUI_MSG_SkinMemoryError));
1078 return 1;
1079 }
1080
1081 return 0;
1082 }
1083
1084 /**
1085 * @brief Parse a @a decoration definition.
1086 *
1087 * Syntax: decoration=enable|disable
1088 *
1089 * @param in definition to be analyzed
1090 *
1091 * @return 0 (ok) or 1 (error)
1092 */
item_decoration(char * in)1093 static int item_decoration(char *in)
1094 {
1095 if (!window_item(currItem))
1096 return 1;
1097
1098 if (in_window("video"))
1099 return 1;
1100 if (in_window("playbar"))
1101 return 1;
1102 if (in_window("menu"))
1103 return 1;
1104
1105 strlower(in);
1106
1107 if (strcmp(in, "enable") != 0 && strcmp(in, "disable") != 0) {
1108 skin_error(_(MSGTR_GUI_MSG_SkinUnknownParameter), in);
1109 return 1;
1110 }
1111
1112 skin->mainDecoration = (strcmp(in, "enable") == 0);
1113
1114 mp_msg(MSGT_GPLAYER, MSGL_DBG2, "[skin] %s: %s\n", currItem, in);
1115
1116 return 0;
1117 }
1118
1119 /**
1120 * @brief Parsing functions responsible for skin item definitions.
1121 */
1122 static _item skinItem[] = {
1123 { "background", item_background },
1124 { "base", item_base },
1125 { "button", item_button },
1126 { "decoration", item_decoration },
1127 { "dlabel", item_dlabel },
1128 { "end", item_end },
1129 { "font", item_font },
1130 { "hpotmeter", item_hpotmeter },
1131 { "menu", item_menu },
1132 { "pimage", item_pimage },
1133 { "potmeter", item_potmeter }, // legacy
1134 { "rpotmeter", item_rpotmeter },
1135 { "section", item_section },
1136 { "selected", item_selected },
1137 { "slabel", item_slabel },
1138 { "vpotmeter", item_vpotmeter },
1139 { "window", item_window }
1140 };
1141
1142 /**
1143 * @brief Read a skin @a image file.
1144 *
1145 * @param fname filename (with path)
1146 * @param img memory location to store the image data
1147 *
1148 * @return return code of #bpRead()
1149 */
skinImageRead(char * fname,guiImage * img)1150 int skinImageRead(char *fname, guiImage *img)
1151 {
1152 int i = bpRead(fname, img);
1153
1154 switch (i) {
1155 case -1:
1156 skin_error(_(MSGTR_GUI_MSG_SkinErrorBitmap16Bit), fname);
1157 break;
1158
1159 case -2:
1160 skin_error(_(MSGTR_GUI_MSG_SkinBitmapNotFound), fname);
1161 break;
1162
1163 case -5:
1164 skin_error(_(MSGTR_GUI_MSG_SkinBitmapPngReadError), fname);
1165 break;
1166
1167 case -8:
1168 skin_error(_(MSGTR_GUI_MSG_SkinBitmapConversionError), fname);
1169 break;
1170 }
1171
1172 return i;
1173 }
1174
1175 /**
1176 * @brief Build the skin file path for a skin name.
1177 *
1178 * @param dir skins directory
1179 * @param sname name of the skin
1180 *
1181 * @return skin file path
1182 *
1183 * @note As a side effect, variable #path gets set to the skin path.
1184 */
setname(char * dir,char * sname)1185 static char *setname(char *dir, char *sname)
1186 {
1187 static char skinfname[512];
1188
1189 av_strlcpy(skinfname, dir, sizeof(skinfname));
1190 av_strlcat(skinfname, "/", sizeof(skinfname));
1191 av_strlcat(skinfname, sname, sizeof(skinfname));
1192 av_strlcat(skinfname, "/", sizeof(skinfname));
1193 av_strlcpy(path, skinfname, sizeof(path));
1194 av_strlcat(skinfname, "skin", sizeof(skinfname));
1195
1196 return skinfname;
1197 }
1198
1199 /**
1200 * @brief Create the most minimal skin possible.
1201 *
1202 * @return 0 (ok) or -1 (error)
1203 */
skinNoskin(void)1204 static int skinNoskin(void)
1205 {
1206 mp_msg(MSGT_GPLAYER, MSGL_DBG2, "[skin] using built-in skin\n");
1207
1208 appFreeStruct();
1209
1210 /* section = movieplayer */
1211
1212 skin = &guiApp;
1213
1214 /* window = main */
1215
1216 currWin = &skin->main;
1217 currWinItemIdx = &skin->IndexOfMainItems;
1218 currWinItems = skin->mainItems;
1219
1220 /* decoration = disable */
1221
1222 skin->mainDecoration = 0;
1223
1224 /* base = <img>, 0, 0 (where <img> is a 1x1 transparent pixel image) */
1225
1226 currWin->Bitmap.Width = 1;
1227 currWin->Bitmap.Height = 1;
1228 currWin->Bitmap.Bpp = 32;
1229 currWin->Bitmap.ImageSize = 4;
1230 currWin->Bitmap.Image = calloc(1, 4);
1231
1232 if (!currWin->Bitmap.Image) {
1233 skin_error(_(MSGTR_GUI_MSG_SkinMemoryError));
1234 return -1;
1235 }
1236
1237 *(uint32_t *)currWin->Bitmap.Image = GUI_TRANSPARENT;
1238
1239 if (!bpRenderMask(&currWin->Bitmap, &currWin->Mask)) {
1240 skin_error(_(MSGTR_GUI_MSG_SkinMemoryError));
1241 return -1;
1242 }
1243
1244 currWin->x = 0;
1245 currWin->y = 0;
1246 currWin->width = currWin->Bitmap.Width;
1247 currWin->height = currWin->Bitmap.Height;
1248
1249 /* window = video */
1250
1251 currWin = &skin->video;
1252 currWinItemIdx = NULL;
1253 currWinItems = NULL;
1254
1255 /* base = NULL, -1, -1, 720, 404 */
1256
1257 currWin->x = -1;
1258 currWin->y = -1;
1259 currWin->width = 720;
1260 currWin->height = 404;
1261
1262 /* background = 0, 0, 0 */
1263
1264 currWin->R = 0;
1265 currWin->G = 0;
1266 currWin->B = 0;
1267
1268 /* end */
1269
1270 return 0;
1271 }
1272
1273 /**
1274 * @brief Read and parse a skin.
1275 *
1276 * @param sname name of the skin
1277 *
1278 * @return 0 (ok), -1 (skin file not found or not readable) or -2 (parsing error)
1279 */
skinRead(char * sname)1280 int skinRead(char *sname)
1281 {
1282 char *skinfname;
1283 FILE *skinfile;
1284 unsigned char line[256];
1285 unsigned char param[256];
1286 unsigned int i;
1287
1288 if (*sname == 0 || strcmp(sname, "Noskin") == 0)
1289 return skinNoskin();
1290
1291 skinfname = setname(skinDirInHome, sname);
1292
1293 if ((skinfile = fopen(skinfname, "rt")) == NULL) {
1294 skinfname = setname(skinDirInData, sname);
1295
1296 if ((skinfile = fopen(skinfname, "rt")) == NULL) {
1297 mp_msg(MSGT_GPLAYER, MSGL_ERR, _(MSGTR_GUI_MSG_SkinFileNotFound), skinfname);
1298 return -1;
1299 }
1300 }
1301
1302 mp_msg(MSGT_GPLAYER, MSGL_DBG2, "[skin] configuration file: %s\n", skinfname);
1303
1304 appFreeStruct();
1305
1306 skin = NULL;
1307 currWinName[0] = 0;
1308 linenumber = 0;
1309
1310 while (fgetstr(line, sizeof(line), skinfile)) {
1311 linenumber++;
1312
1313 strswap(line, '\t', ' ');
1314 despace(line);
1315 decomment(line);
1316
1317 if (!*line)
1318 continue;
1319
1320 cutStr(line, currItem, '=', 0);
1321 cutStr(line, param, '=', 1);
1322 strlower(currItem);
1323
1324 for (i = 0; i < FF_ARRAY_ELEMS(skinItem); i++) {
1325 if (strcmp(currItem, skinItem[i].name) == 0) {
1326 if (skinItem[i].func(param) != 0) {
1327 fclose(skinfile);
1328 return -2;
1329 } else
1330 break;
1331 }
1332 }
1333
1334 if (i == FF_ARRAY_ELEMS(skinItem)) {
1335 skin_error(_(MSGTR_GUI_MSG_SkinUnknownItem), currItem);
1336 fclose(skinfile);
1337 return -2;
1338 }
1339 }
1340
1341 fclose(skinfile);
1342
1343 if (linenumber == 0) {
1344 mp_msg(MSGT_GPLAYER, MSGL_ERR, _(MSGTR_GUI_MSG_SkinFileNotReadable), skinfname);
1345 return -1;
1346 }
1347
1348 return 0;
1349 }
1350