1 //
2 // anyRemote
3 // a wi-fi or bluetooth remote for your PC.
4 //
5 // Copyright (C) 2006-2016 Mikhail Fedotov <anyremote@mail.ru>
6 //
7 // This program is free software; you can redistribute it and/or modify
8 // it under the terms of the GNU General Public License as published by
9 // the Free Software Foundation; either version 3 of the License, or
10 // (at your option) any later version.
11 //
12 // This program is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 // GNU General Public License for more details.
16 //
17 // You should have received a copy of the GNU General Public License
18 // along with this program; if not, write to the Free Software
19 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20 //
21 
22 #include <ctype.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <sys/stat.h>
27 #include <fcntl.h>
28 
29 #include "str.h"
30 #include "utils.h"
31 #include "conf.h"
32 #include "list.h"
33 #include "state.h"
34 #include "mutex.h"
35 #include "gen_xml.h"
36 #include "var.h"
37 
38 extern char tmp[MAXMAXLEN];
39 
40 boolean_t _stateInited = BOOL_NO;
41 static ClientState _state;
42 static SingleList* _userMenu = NULL;
43 static int         _curForm  = CF;
44 
45 int _iconPadding   = 0;
46 int _iconSize      = 64;
47 
initState()48 void initState()
49 {
50     INFO2("[DS]: initState");
51 
52     mutexNew(M_STATE);
53 
54 
55     _state.cf.layout7x1   = BOOL_NO;
56     _state.cf.upBtn       = NULL;
57     _state.cf.dnBtn       = NULL;
58     _state.cf.joystick    = BOOL_YES;
59     _state.cf.keypad      = BOOL_YES;
60 
61     _state.cf.caption     = stringNew("anyRemote");
62     _state.cf.title       = stringNew(" ");
63     _state.cf.status      = stringNew(" ");
64     int i = 0;
65     for (;i<ICON_NUM;i++) {
66         _state.cf.icons[i] = stringNew("default");
67         _state.cf.hints[i] = stringNew("");
68     }
69     _state.cf.cover          = NULL;
70     _state.cf.namedCover  = NULL;
71     _state.cf.volume      = NULL;
72     _state.cf.useVolume   = BOOL_NO;
73     _state.cf.visual.font = stringNew("small");
74     _state.cf.visual.fg   = stringNew("FFFFFF");
75     _state.cf.visual.bg   = stringNew("000000");
76 
77     _state.lf.caption     = stringNew("");
78     _state.lf.items       = NULL;
79     _state.lf.selIdx      = 1;
80     _state.lf.visual.font = stringNew("medium");
81     _state.lf.visual.fg   = stringNew("000000");
82     _state.lf.visual.bg   = stringNew("FFFFFF");
83 
84     _state.tf.caption     = stringNew("");
85     _state.tf.text        = stringNew("");
86     _state.tf.visual.font = stringNew("small");
87     _state.tf.visual.fg   = stringNew("000000");
88     _state.tf.visual.bg   = stringNew("FFFFFF");
89 
90     _state.wf.window      = NULL;
91 
92     _state.ef.caption     = stringNew("");
93     _state.ef.text        = stringNew("");
94     _state.ef.label       = stringNew("");
95 
96     _stateInited = BOOL_YES;
97 }
98 
listItemFree(pointer_t data)99 static void listItemFree(pointer_t data)
100 {
101     ListItem* item = (ListItem*) data;
102     if (item->icon) {
103         stringFree(item->icon,   (item->icon->str   != NULL));
104     }
105     if (item->string) {
106         stringFree(item->string, (item->string->str != NULL));
107     }
108     free(item);
109 }
110 
freeState()111 void freeState()
112 {
113     if (_stateInited == BOOL_NO) {
114         return;
115     }
116 
117     INFO2("[DS]: freeState");
118 
119     mutexLock(M_STATE);
120 
121     if (_state.cf.caption) stringFree(_state.cf.caption, BOOL_YES);
122     if (_state.cf.title)   stringFree(_state.cf.title,   BOOL_YES);
123     if (_state.cf.status)  stringFree(_state.cf.status,  BOOL_YES);
124 
125     if (_state.cf.upBtn)   stringFree(_state.cf.upBtn,   BOOL_YES);
126     if (_state.cf.dnBtn)   stringFree(_state.cf.dnBtn,   BOOL_YES);
127 
128     int i = 0;
129     for (;i<ICON_NUM;i++) {
130         if (_state.cf.icons[i]) stringFree(_state.cf.icons[i], BOOL_YES);
131         if (_state.cf.hints[i]) stringFree(_state.cf.hints[i], BOOL_YES);
132     }
133 
134     freeCfCover();
135 
136     if (_state.cf.volume)      stringFree(_state.cf.volume,      BOOL_YES);
137     if (_state.cf.visual.font) stringFree(_state.cf.visual.font, BOOL_YES);
138     if (_state.cf.visual.fg)   stringFree(_state.cf.visual.fg,   BOOL_YES);
139     if (_state.cf.visual.bg)   stringFree(_state.cf.visual.bg,   BOOL_YES);
140 
141     if (_state.lf.caption)     stringFree(_state.lf.caption,     BOOL_YES);
142 
143     freeLfList();
144 
145     if (_state.lf.visual.font) stringFree(_state.lf.visual.font, BOOL_YES);
146     if (_state.lf.visual.fg)   stringFree(_state.lf.visual.fg,   BOOL_YES);
147     if (_state.lf.visual.bg)   stringFree(_state.lf.visual.bg,   BOOL_YES);
148 
149     if (_state.tf.caption)     stringFree(_state.tf.caption,     BOOL_YES);
150     if (_state.tf.text)        stringFree(_state.tf.text,        BOOL_YES);
151     if (_state.tf.visual.font) stringFree(_state.tf.visual.font, BOOL_YES);
152     if (_state.tf.visual.fg)   stringFree(_state.tf.visual.fg,   BOOL_YES);
153     if (_state.tf.visual.bg)   stringFree(_state.tf.visual.bg,   BOOL_YES);
154 
155     if (_state.wf.window)      stringFree(_state.wf.window,      BOOL_YES);
156 
157     if (_state.ef.caption)     stringFree(_state.ef.caption,     BOOL_YES);
158     if (_state.ef.text)        stringFree(_state.ef.text,        BOOL_YES);
159     if (_state.ef.label)       stringFree(_state.ef.label,       BOOL_YES);
160 
161     freeMenu();
162 
163     mutexUnlock(M_STATE);
164 
165     mutexRemove(M_STATE);
166 
167     _stateInited = BOOL_NO;
168 }
169 
170 // rely on previous strtok()
setFgBg(int form,boolean_t set_fg)171 void setFgBg(int form, boolean_t set_fg)
172 {
173     char buf[16];
174 
175     char *token = strtok(NULL,",");
176     if (!token) {
177         return;
178     }
179     int r = atoi(token);
180 
181     token = strtok(NULL,",");
182     if (!token) {
183         return;
184     }
185     int g = atoi(token);
186 
187     token = strtok(NULL,",");
188     if (!token) {
189         return;
190     }
191     int b = atoi(token);
192 
193     sprintf(buf,"%0*x%0*x%0*x",2,r,2,g,2,b);
194 
195     INFO2("[DS]: setFgBg fg=%d form=%d color=%s",set_fg,form,buf);
196 
197     string_t** ptr = NULL;
198 
199     if (form == CF) {
200         ptr = (set_fg ? &_state.cf.visual.fg : &_state.cf.visual.bg);
201     } else if (form == TX) {
202         ptr = (set_fg ? &_state.tf.visual.fg : &_state.tf.visual.bg);
203     } else if (form == LI) {
204         ptr = (set_fg ? &_state.lf.visual.fg : &_state.lf.visual.bg);
205     }
206 
207     if (ptr) {
208         if ((*ptr)) {
209             (*ptr) = stringNew(buf);
210         } else {
211             stringAssign((*ptr),buf);
212         }
213     }
214 }
215 
setFont(int form)216 void setFont(int form)
217 {
218     char *s = strtok(NULL,",");
219     INFO2("[DS]: setFont %d %s",form,(s?s:"NULL"));
220 
221     if (!s) {
222         return;
223     }
224 
225     while (isspace(*s)) {
226         ++s;
227     }
228 
229     string_t** ptr = NULL;
230 
231     if (form == CF) {
232         ptr = &_state.cf.visual.font;
233     } else if (form == TX) {
234         ptr = &_state.tf.visual.font;
235     } else if (form == LI) {
236         ptr = &_state.lf.visual.font;
237     }
238 
239     if (ptr) {
240         if ((*ptr)) {
241             (*ptr) = stringNew(s);
242         } else {
243             stringAssign((*ptr),s);
244         }
245     }
246 }
247 
setCurForm(int f)248 void setCurForm(int f)
249 {
250     _curForm = f;
251 }
252 
curForm()253 int curForm()
254 {
255     return _curForm;
256 }
257 
258 ////////////////////////////////////////////////////////////////////////////////
259 //
260 //  Menu
261 //
262 ////////////////////////////////////////////////////////////////////////////////
263 
addDefMenu()264 static void addDefMenu()
265 {
266     INFO2("[DS]: addDefMenu %d", _curForm);
267 
268     switch (_curForm) {
269 
270         case TX:
271         case LI:
272             _userMenu = listSingleAppend(_userMenu, stringNew("Back"));
273             break;
274 
275         case EF:
276             _userMenu = listSingleAppend(_userMenu, stringNew("Back"));
277             _userMenu = listSingleAppend(_userMenu, stringNew("Ok"));
278             break;
279 
280         case CF:
281             //_userMenu = listSingleAppend(_userMenu, stringNew("Disconnect"));
282             //_userMenu = listSingleAppend(_userMenu, stringNew("Exit"));
283             break;
284 
285         case WM:
286             break;
287     }
288 }
289 
stringItemFree(pointer_t data)290 static void stringItemFree(pointer_t data)
291 {
292     stringFree(data,(((string_t*) data)->str != NULL));
293 }
294 
freeMenu()295 void freeMenu()
296 {
297     INFO2("[DS]: freeMenu %d", _curForm);
298     listSingleFullFree(_userMenu, stringItemFree);
299     _userMenu = NULL;
300 }
301 
setupDefMenu()302 void setupDefMenu()
303 {
304     INFO2("[DS]: setupDefMenu %d", _curForm);
305     mutexLock(M_STATE);
306 
307     freeMenu();
308     addDefMenu();
309 
310     mutexUnlock(M_STATE);
311 }
312 
userMenu()313 SingleList* userMenu()
314 {
315     return _userMenu;
316 }
317 
menuSize()318 int menuSize()
319 {
320     return (_userMenu ? listSingleLength(_userMenu) : 0);
321 }
322 
menuNth(int n)323 SingleList* menuNth(int n)
324 {
325     return (_userMenu ? listSingleNth(_userMenu, n) : NULL);
326 }
327 
setMenu()328 void setMenu()
329 {
330     char *token = strtok(NULL,",");
331     if (!token) {
332         return;
333     }
334     while (isspace(*token)) {
335         ++token;
336     }
337 
338     if (strncmp(token,"add",3) == 0 || strncmp(token,"replace",7) == 0) {
339 
340         logger(L_INF, "[DS]: setMenu add/replace");
341 
342         if (strcmp(token,"replace") == 0) {
343             setupDefMenu();
344         }
345 
346         char *token3 = strtok(NULL,",");
347         while (token3) {
348 
349             while (isspace(*token3)) {
350                 ++token3;
351             }
352 
353             INFO2("[DS]: Set(menu, ...) %s", token3);
354 
355             mutexLock(M_STATE);
356 
357             string_t* dup = stringNew(token3);
358             _userMenu = listSingleAppend(_userMenu, dup);
359             token3 = strtok(NULL,",");
360 
361             mutexUnlock(M_STATE);
362         }
363     } else if (strncmp(token,"clear",5) == 0) {
364         mutexLock(M_STATE);
365         freeMenu();
366         mutexUnlock(M_STATE);
367     } else {
368         ERROR2("[DS]: Can to parse Set(menu, ...)");
369     }
370 }
371 
372 ////////////////////////////////////////////////////////////////////////////////
373 //
374 //  Control form
375 //
376 ////////////////////////////////////////////////////////////////////////////////
377 
setCfCaption(const char * s)378 void setCfCaption(const char * s)
379 {
380     mutexLock(M_STATE);
381 
382     if (_state.cf.caption == NULL) {
383         _state.cf.caption = stringNew(s);
384     } else {
385         stringAssign(_state.cf.caption,s);
386     }
387 
388     mutexUnlock(M_STATE);
389 }
390 
cfCaption()391 const char* cfCaption()
392 {
393     return (_state.cf.caption ? _state.cf.caption->str : NULL);
394 }
395 
setCfTitle(const char * s)396 void setCfTitle(const char * s)
397 {
398     boolean_t setupMenu = (_curForm != CF);
399 
400     mutexLock(M_STATE);
401 
402     if (_state.cf.title == NULL) {
403         _state.cf.title =  stringNew(s);
404     } else {
405         stringAssign(_state.cf.title,s);
406     }
407     mutexUnlock(M_STATE);
408 
409     setCurForm(CF);
410 
411     if (setupMenu) {
412         setupDefMenu();
413     }
414 }
415 
cfTitle()416 const char* cfTitle()
417 {
418     return (_state.cf.title ? _state.cf.title->str : NULL);
419 }
420 
setCfStatus(const char * s)421 void setCfStatus(const char * s)
422 {
423     boolean_t setupMenu = (_curForm != CF);
424 
425     mutexLock(M_STATE);
426 
427     if (_state.cf.status == NULL) {
428         _state.cf.status =  stringNew(s);
429     } else {
430         stringAssign(_state.cf.status,s);
431     }
432 
433     mutexUnlock(M_STATE);
434 
435     setCurForm(CF);
436 
437     if (setupMenu) {
438         setupDefMenu();
439     }
440 }
441 
cfStatus()442 const char* cfStatus()
443 {
444     return (_state.cf.status ? _state.cf.status->str : NULL);
445 }
446 
setIcons()447 void setIcons()
448 {
449     boolean_t setupMenu = (curForm() != CF);
450 
451     char* token = strtok(NULL,",");
452 
453     if (token && strcmp(token,"SAME") != 0) {
454         setCfCaption(token);
455     }
456 
457     token = strtok(NULL,",");
458 
459     mutexLock(M_STATE);
460 
461     while (token) {
462 
463         while (isspace(*token)) {
464             ++token;
465         }
466 
467         int ic = -1;
468         if (strncmp(token,"*",1) == 0) {
469             ic = 9;
470         } else if (strncmp(token,"#",1) == 0) {
471             ic = 11;
472         } else if (strncmp(token,"0",1) == 0) {
473             ic = 10;
474         } else {
475             ic = atoi(token)-1;
476         }
477         if (ic >=0 && ic < 12) {
478             token = strtok(NULL,",");
479             while (token && isspace(*token)) {
480                 ++token;
481             }
482 
483              if (_state.cf.icons[ic] == NULL) {
484                 _state.cf.icons[ic] =  stringNew((token?token:"none"));
485             } else {
486                 stringAssign(_state.cf.icons[ic],(token?token:"none"));
487             }
488             INFO2("[DS]: setIcons %d %s",ic,_state.cf.icons[ic]->str);
489         }
490         token = strtok(NULL,",");
491     }
492 
493     mutexUnlock(M_STATE);
494 
495     setCurForm(CF);
496 
497     if (setupMenu) {
498         setupDefMenu();
499     }
500 
501 }
502 
setHints()503 void setHints()
504 {
505     char* token = strtok(NULL,",");
506 
507     mutexLock(M_STATE);
508 
509     while (token) {
510 
511         while (isspace(*token)) {
512             ++token;
513         }
514 
515         //DEBUG2("[DS]: setHints %s",token);
516 
517         int ic = -1;
518         if (strncmp(token,"*",1) == 0) {
519             ic = 9;
520         } else if (strncmp(token,"#",1) == 0) {
521             ic = 11;
522         } else if (strncmp(token,"0",1) == 0) {
523             ic = 10;
524         } else {
525             ic = atoi(token)-1;
526         }
527 
528         if (ic >=0 && ic < 12) {
529             token = strtok(NULL,",");
530             while (token && isspace(*token)) {
531                 ++token;
532             }
533 
534              if (_state.cf.hints[ic] == NULL) {
535                 _state.cf.hints[ic] =  stringNew((token?token:""));
536             } else {
537                 stringAssign(_state.cf.hints[ic],(token?token:""));
538             }
539             INFO2("[DS]: setHints %d %s",ic,_state.cf.hints[ic]->str);
540         }
541         token = strtok(NULL,",");
542     }
543 
544     mutexUnlock(M_STATE);
545 }
546 
setUseVolume(boolean_t use)547 static void setUseVolume(boolean_t use)
548 {
549     _state.cf.useVolume = use;
550 }
551 
552 //
553 // Set(skin,default|bottomline|3x4|7x1
554 //    [,keypad_only|joystick_only]
555 //    [,ticker|noticker]   <-- TODO
556 //    [,volume]
557 //    [,choose,_button_]   <-- TODO
558 //    [,up,_button_] [,down,_button_])
559 //
setSkin()560 void setSkin()
561 {
562 
563     setUseVolume(BOOL_NO);  // reset volume usage flag
564     _state.cf.layout7x1 = BOOL_NO;
565 
566     char* token;
567 
568     boolean_t setupMenu = (curForm() != CF);
569 
570     mutexLock(M_STATE);
571 
572     if (_state.cf.upBtn) {
573         stringFree(_state.cf.upBtn, BOOL_YES);
574         _state.cf.upBtn = NULL;
575     }
576     if (_state.cf.dnBtn) {
577         stringFree(_state.cf.dnBtn, BOOL_YES);
578         _state.cf.dnBtn = NULL;
579     }
580 
581     while((token = strtok(NULL,","))) {
582 
583         if (strcmp(token,"bottomline") == 0 ||
584             strcmp(token,"7x1") == 0 ||
585             strcmp(token,"6x1") == 0 ||
586             strcmp(token,"5x1") == 0) {
587             _state.cf.layout7x1 = BOOL_YES;
588         } else if (strcmp(token,"volume") == 0) {
589             setUseVolume(BOOL_YES);
590         } else if (strcmp(token,"keypad_only") == 0) {
591             _state.cf.keypad   = BOOL_YES;
592             _state.cf.joystick = BOOL_NO;
593         } else if (strcmp(token,"joystick_only") == 0) {
594             _state.cf.keypad   = BOOL_NO;
595             _state.cf.joystick = BOOL_YES;
596         } else if (strcmp(token,"up") == 0) {
597             token = strtok(NULL,",");
598             if (token) {
599                 if (_state.cf.upBtn == NULL) {
600                     _state.cf.upBtn =  stringNew(token);
601                 } else {
602                     stringAssign(_state.cf.upBtn,token);
603                 }
604             }
605         } else if (strcmp(token,"down") == 0) {
606             token = strtok(NULL,",");
607             if (token) {
608                 if (_state.cf.dnBtn == NULL) {
609                     _state.cf.dnBtn =  stringNew(token);
610                 } else {
611                     stringAssign(_state.cf.dnBtn,token);
612                 }
613             }
614         }
615     }
616 
617     //if (_state.cf.layout7x1) {
618     //    freeCfCover();
619     //}
620 
621     mutexUnlock(M_STATE);
622 
623     setCurForm(CF);
624 
625     if (setupMenu) {
626         setupDefMenu();
627     }
628 }
629 
cfIcon(int i)630 const char* cfIcon(int i)
631 {
632     if (i < 0 || i >= ICON_NUM) return NULL;
633 
634     return (_state.cf.icons[i] ? _state.cf.icons[i]->str : NULL);
635 }
636 
cfHint(int i)637 const char* cfHint(int i)
638 {
639     if (i < 0 || i >= ICON_NUM) return NULL;
640 
641     return (_state.cf.hints[i] ? _state.cf.hints[i]->str : NULL);
642 }
643 
setCfCover(const char * s)644 void setCfCover(const char * s)
645 {
646     //printf("setCfCover %s\n", s);
647     mutexLock(M_STATE);
648 
649     if (s) {
650         if (_state.cf.cover == NULL) {
651             _state.cf.cover =  stringNew(s);
652         } else {
653             stringAssign(_state.cf.cover,s);
654         }
655         if (_state.cf.namedCover) {
656             stringFree(_state.cf.namedCover, BOOL_YES);
657             _state.cf.namedCover = NULL;
658         }
659     } else {
660         freeCfCover();
661     }
662     mutexUnlock(M_STATE);
663 }
664 
setCfNamedCover(char * s)665 void setCfNamedCover(char* s)
666 {
667     //printf("setCfNamedCover %s\n", s);
668     mutexLock(M_STATE);
669 
670     if (s) {
671         if (_state.cf.namedCover == NULL) {
672             _state.cf.namedCover =  stringNew(s);
673         } else {
674             stringAssign(_state.cf.namedCover,s);
675         }
676         if (_state.cf.cover) {
677             stringFree(_state.cf.cover, BOOL_YES);
678             _state.cf.cover = NULL;
679         }
680     } else {
681         freeCfCover();
682     }
683     mutexUnlock(M_STATE);
684 }
685 
cfCover()686 const char* cfCover()
687 {
688     return (_state.cf.cover ? _state.cf.cover->str : NULL);
689 }
690 
cfNamedCover()691 const char* cfNamedCover()
692 {
693     return (_state.cf.namedCover ? _state.cf.namedCover->str : NULL);
694 }
695 
freeCfCover()696 void freeCfCover()
697 {
698     //printf("freeCfCover\n");
699     if (_state.cf.cover)      stringFree(_state.cf.cover,      BOOL_YES);
700     if (_state.cf.namedCover) stringFree(_state.cf.namedCover, BOOL_YES);
701     _state.cf.cover = NULL;
702     _state.cf.namedCover = NULL;
703 }
704 
705 // cover-audio -> /usr/share/..../cover-audio.png
findNamedCover(const char * name)706 string_t* findNamedCover(const char *name)
707 {
708     char *confDir = dupVarValue(VAR_CFGDIR);
709     string_t* fpath = confDir ? stringNew(confDir) : stringNew("");
710     if (confDir) free(confDir);
711 
712     stringAppend(fpath, "/Icons/common/");
713     stringAppend(fpath, name);
714     stringAppend(fpath, ".png");
715 
716     struct stat statbuf;
717 
718     if (stat(fpath->str, &statbuf) < 0) {  // not found
719 
720         char *h = getenv("HOME");
721         if (h) {
722             stringAssign(fpath, h);
723             stringAppend(fpath, "/.anyRemote/Covers/");
724             stringAppend(fpath, name);
725             stringAppend(fpath, ".png");
726             if (stat(fpath->str, &statbuf) < 0) {
727                 stringFree(fpath,  BOOL_YES);
728                 return NULL;
729             }
730         } else {
731             stringFree(fpath,  BOOL_YES);
732             return NULL;
733         }
734     }
735 
736     if (S_ISREG(statbuf.st_mode) || S_ISLNK(statbuf.st_mode)) {
737         return fpath;
738     }
739 
740     stringFree(fpath,  BOOL_YES);
741     return NULL;
742 }
743 
cfBg()744 const char* cfBg()
745 {
746     return (_state.cf.visual.bg ? _state.cf.visual.bg->str : NULL);
747 }
748 
cfFg()749 const char* cfFg()
750 {
751     return (_state.cf.visual.fg ? _state.cf.visual.fg->str : NULL);
752 }
753 
cfFont()754 const char* cfFont()
755 {
756     return (_state.cf.visual.font ? _state.cf.visual.font->str : NULL);
757 }
758 
setCfVolume(const char * s)759 void setCfVolume(const char * s)
760 {
761     if (s) {
762         if (_state.cf.volume == NULL) {
763             _state.cf.volume =  stringNew(s);
764         } else {
765             stringAssign(_state.cf.volume,s);
766         }
767     } else {
768         setCfVolume("");
769     }
770 }
771 
cfVolume()772 const char* cfVolume()
773 {
774     return (_state.cf.volume ? _state.cf.volume->str : NULL);
775 }
776 
useVolume()777 boolean_t useVolume()
778 {
779     return _state.cf.useVolume;
780 }
781 
bottomlineSkin()782 boolean_t bottomlineSkin()
783 {
784     return _state.cf.layout7x1;
785 }
786 
cfUpButton()787 const char* cfUpButton()
788 {
789     return (_state.cf.upBtn ? _state.cf.upBtn->str : NULL);
790 }
791 
cfDownButton()792 const char* cfDownButton()
793 {
794     return (_state.cf.dnBtn ? _state.cf.dnBtn->str : NULL);
795 }
796 
useKeypad()797 boolean_t useKeypad()
798 {
799     return _state.cf.keypad;
800 }
801 
useJoystick()802 boolean_t useJoystick()
803 {
804     return _state.cf.joystick;
805 }
806 
807 ////////////////////////////////////////////////////////////////////////////////
808 //
809 //  List form
810 //
811 ////////////////////////////////////////////////////////////////////////////////
812 
setLfCaption(const char * s)813 static void setLfCaption(const char * s)
814 {
815     if (_state.lf.caption == NULL) {
816         _state.lf.caption =  stringNew(s);
817     } else {
818         stringAssign(_state.lf.caption,s);
819     }
820 }
821 
lfCaption()822 const char* lfCaption()
823 {
824     return (_state.lf.caption ? _state.lf.caption->str : NULL);
825 }
826 
827 // rely on previous strtok()
setLfList(boolean_t useIcons)828 static void setLfList(boolean_t useIcons)
829 {
830     char *token3 = strtok(NULL,",\n");
831     while (token3) {
832 
833         if (strlen(token3) > 0) {   // avoid empty list item
834 
835             ListItem * item = malloc(sizeof(ListItem));
836 
837             char * semicolon = index(token3,':');
838             if (useIcons && semicolon) {
839                 *semicolon = '\0';
840                 semicolon++;
841                 item->icon   = stringNew(token3);
842                 item->string = stringNew(semicolon);
843             } else {
844                 item->icon   = NULL;
845                 item->string = stringNew(token3);
846             }
847 
848             _state.lf.items = listSingleAppend(_state.lf.items, item);
849         }
850         token3 = strtok(NULL,",\n");
851     }
852 }
853 
setLfIndex(const char * s)854 static void setLfIndex(const char * s)
855 {
856     _state.lf.selIdx = (s ? atoi(s) : 1);
857 }
858 
setList(boolean_t useIcons)859 void setList(boolean_t useIcons)
860 {
861     logger(L_INF, "[DS]: setList");
862     boolean_t setupMenu = (_curForm != LI);
863 
864     char *token = strtok(NULL,",");
865     if (!token) {
866         return;
867     }
868     while (isspace(*token)) {
869         ++token;
870     }
871 
872 
873     if (strncmp(token,"add",3) == 0 || strncmp(token,"replace",7) == 0) {
874 
875         mutexLock(M_STATE);
876         _curForm = LI;
877 
878         char *token2 = strtok(NULL,","); // can be NULL
879         if (!token2) {
880             return;
881         }
882 
883         logger(L_INF, "[DS]: setList add/replace");
884 
885         if (strcmp(token,"replace") == 0) {
886             freeLfList();
887         }
888 
889         if (strcmp(token2,"SAME") != 0) {
890             setLfCaption(token2);
891         }
892 
893         setLfList(useIcons);  // rely on previous strtok()
894         mutexUnlock(M_STATE);
895 
896     } else if (strncmp(token,"close",5) == 0) {
897 
898         char *token2 = strtok(NULL,","); // can be NULL
899         if (token2 && strcmp(token,"clear") == 0) {
900             mutexLock(M_STATE);
901             freeLfList();
902             mutexUnlock(M_STATE);
903         }
904         _curForm = CF;
905         setupDefMenu();
906         return;
907     } else if (strncmp(token,"clear",5) == 0) {
908         mutexLock(M_STATE);
909         freeLfList();
910         mutexUnlock(M_STATE);
911         return;
912     } else if (strncmp(token,"fg",2) == 0) {
913         mutexLock(M_STATE);
914         setFgBg(LI,BOOL_YES);
915         mutexUnlock(M_STATE);
916         return;
917     } else if (strncmp(token,"bg",2) == 0) {
918         mutexLock(M_STATE);
919         setFgBg(LI,BOOL_NO);
920         mutexUnlock(M_STATE);
921         return;
922     } else if (strncmp(token,"font",4) == 0) {
923         mutexLock(M_STATE);
924         setFont(LI);
925         mutexUnlock(M_STATE);
926         return;
927     } else if (strncmp(token,"select",6) == 0) {
928         mutexLock(M_STATE);
929         _curForm = LI;
930         setLfIndex(strtok(NULL,","));
931         mutexUnlock(M_STATE);
932     } else if (strncmp(token,"show",4) == 0) {
933         _curForm = LI;
934     } else {
935         ERROR2("[DS]: Can to parse Set([icon]list, ...)");
936         return;
937     }
938 
939     if (setupMenu) {
940         setupDefMenu();
941     }
942 }
943 
lfIndex()944 int lfIndex()
945 {
946     return _state.lf.selIdx;
947 }
948 
lfBg()949 const char* lfBg()
950 {
951     return (_state.lf.visual.bg ? _state.lf.visual.bg->str : NULL);
952 }
953 
lfFg()954 const char* lfFg()
955 {
956     return (_state.lf.visual.fg ? _state.lf.visual.fg->str : NULL);
957 }
958 
lfFont()959 const char* lfFont()
960 {
961     return (_state.lf.visual.font ? _state.lf.visual.font->str : NULL);
962 }
963 
freeLfList()964 void freeLfList()
965 {
966     if (_state.lf.items) {
967         listSingleFullFree(_state.lf.items, listItemFree);
968         _state.lf.items = NULL;
969         _state.lf.selIdx = 1;
970     }
971 }
972 
lfList()973 SingleList* lfList()
974 {
975     return (_state.lf.items ? _state.lf.items : NULL);
976 }
977 
lfListNth(int n)978 SingleList* lfListNth(int n)
979 {
980     return (_state.lf.items ? listSingleNth(_state.lf.items, n) : NULL);
981 }
982 
lfSize()983 int lfSize()
984 {
985     return (_state.lf.items ? listSingleLength(_state.lf.items) : 0);
986 }
987 
988 ////////////////////////////////////////////////////////////////////////////////
989 //
990 //  Text form
991 //
992 ////////////////////////////////////////////////////////////////////////////////
993 
setTfCaption(const char * s)994 static void setTfCaption(const char * s)
995 {
996     if (_state.tf.caption == NULL) {
997         _state.tf.caption =  stringNew(s);
998     } else {
999         stringAssign(_state.tf.caption,s);
1000     }
1001 }
1002 
tfCaption()1003 const char* tfCaption()
1004 {
1005     return (_state.tf.caption ? _state.tf.caption->str : NULL);
1006 }
1007 
addTfText(const char * s)1008 static void addTfText(const char * s)
1009 {
1010     //INFO2("[DS]: addTfText %s", s); can be too long
1011     if (_state.tf.text == NULL) {
1012         _state.tf.text =  stringNew(s);
1013     } else {
1014         stringAppend(_state.tf.text,s);
1015     }
1016 }
1017 
resetTfText()1018 static void resetTfText()
1019 {
1020     if (_state.tf.text) {
1021         stringTruncate(_state.tf.text,0);
1022     }
1023 }
1024 
tfText()1025 const char* tfText()
1026 {
1027     return (_state.tf.text ? _state.tf.text->str : NULL);
1028 }
1029 
tfBg()1030 const char* tfBg()
1031 {
1032     return (_state.tf.visual.bg ? _state.tf.visual.bg->str : NULL);
1033 }
1034 
tfFg()1035 const char* tfFg()
1036 {
1037     return (_state.tf.visual.fg ? _state.tf.visual.fg->str : NULL);
1038 }
1039 
tfFont()1040 const char* tfFont()
1041 {
1042     return (_state.tf.visual.font ? _state.tf.visual.font->str : NULL);
1043 }
1044 
setText(boolean_t split)1045 void setText(boolean_t split)
1046 {
1047     //logger(L_INF, "[DS]: setText");
1048     boolean_t setupMenu = (_curForm != TX);
1049 
1050     char *token = strtok(NULL,",");
1051     if (!token) {
1052         return;
1053     }
1054     while (isspace(*token)) {
1055         ++token;
1056     }
1057 
1058 
1059     if (strncmp(token,"add",3) == 0 || strncmp(token,"replace",7) == 0) {
1060 
1061         mutexLock(M_STATE);
1062         _curForm = TX;
1063 
1064         char *token2 = strtok(NULL,","); // can be NULL
1065         if (!token2) {
1066             return;
1067         }
1068 
1069         //logger(L_INF, "[DS]: setText add/replace");
1070 
1071         if (strncmp(token,"replace",7) == 0) {
1072             resetTfText();
1073         }
1074 
1075         if (strcmp(token2,"SAME") != 0) {
1076             setTfCaption(token2);
1077         }
1078 
1079         if (split) {
1080             char *token3 = strtok(NULL,"\n");
1081             while (token3) {
1082 
1083                 //INFO2("[DS]: Set(text, ...) %s", token3);
1084                 addTfText(token3);
1085                 token3 = strtok(NULL,"\n");
1086             }
1087         } else {
1088             addTfText(token2 + strlen(token2) + 1);
1089         }
1090         mutexUnlock(M_STATE);
1091 
1092     } else if (strcmp(token,"close") == 0) {
1093 
1094         char *token2 = strtok(NULL,","); // can be NULL
1095         if (token2 && strcmp(token,"clear") == 0) {
1096             mutexLock(M_STATE);
1097             resetTfText();
1098             mutexUnlock(M_STATE);
1099         }
1100 
1101         _curForm = CF;
1102         setupDefMenu();
1103         return;
1104 
1105     } else if (strncmp(token,"clear",5) == 0) {
1106         mutexLock(M_STATE);
1107         resetTfText();
1108         mutexUnlock(M_STATE);
1109         return;
1110     } else if (strncmp(token,"fg",2) == 0) {
1111         mutexLock(M_STATE);
1112         setFgBg(TX,BOOL_YES);
1113         mutexUnlock(M_STATE);
1114         return;
1115     } else if (strncmp(token,"bg",2) == 0) {
1116         mutexLock(M_STATE);
1117         setFgBg(TX,BOOL_NO);
1118         mutexUnlock(M_STATE);
1119         return;
1120     } else if (strncmp(token,"font",4) == 0) {
1121         mutexLock(M_STATE);
1122         setFont(TX);
1123         mutexUnlock(M_STATE);
1124         return;
1125     } else if (strncmp(token,"show",4) == 0) {
1126         _curForm = TX;
1127     } else {
1128         ERROR2("[DS]: Can to parse Set(text, ...)");
1129         return;
1130     }
1131 
1132     if (setupMenu) {
1133         setupDefMenu();
1134     }
1135 }
1136 
1137 ////////////////////////////////////////////////////////////////////////////////
1138 //
1139 //  Edit form
1140 //
1141 ////////////////////////////////////////////////////////////////////////////////
1142 
setEfCaption(const char * s)1143 static void setEfCaption(const char * s)
1144 {
1145     if (_state.ef.caption == NULL) {
1146         _state.ef.caption =  stringNew(s);
1147     } else {
1148         stringAssign(_state.ef.caption,s);
1149     }
1150 }
1151 
efCaption()1152 const char* efCaption()
1153 {
1154     return (_state.ef.caption ? _state.ef.caption->str : NULL);
1155 }
1156 
setEfLabel(const char * s)1157 static void setEfLabel(const char * s)
1158 {
1159     if (_state.ef.label == NULL) {
1160         _state.ef.label =  stringNew(s);
1161     } else {
1162         stringAssign(_state.ef.label,s);
1163     }
1164 }
1165 
efLabel()1166 const char* efLabel()
1167 {
1168     return (_state.ef.label ? _state.ef.label->str : NULL);
1169 }
1170 
setEfText(const char * s)1171 static void setEfText(const char * s)
1172 {
1173     if (_state.ef.text == NULL) {
1174         _state.ef.text =  stringNew(s);
1175     } else {
1176         stringAssign(_state.ef.text,s);
1177     }
1178 }
1179 
efText()1180 const char* efText()
1181 {
1182     return (_state.ef.text ? _state.ef.text->str : NULL);
1183 }
1184 
setEfPassword(boolean_t use)1185 void setEfPassword(boolean_t use)
1186 {
1187     _state.ef.pass = use;
1188 }
1189 
efPassword()1190 boolean_t efPassword()
1191 {
1192     return _state.ef.pass;
1193 }
1194 
1195 // rely on previous strtok()
setEditfield()1196 void setEditfield()
1197 {
1198     logger(L_INF, "[DS]: setEditfield");
1199 
1200     boolean_t setupMenu = (_curForm != EF);
1201 
1202     char *token1 = strtok(NULL,",");
1203     if (!token1) {
1204         return;
1205     }
1206 
1207     char *token2 = strtok(NULL,",");
1208     if (!token2) {
1209         return;
1210     }
1211 
1212     char *token3 = strtok(NULL,",");  // can be NULL
1213 
1214     mutexLock(M_STATE);
1215 
1216     setEfCaption(token1);
1217     setEfLabel  (token2);
1218     setEfText   (token3 ? token3 : "");
1219     setEfPassword(BOOL_NO);
1220 
1221     mutexUnlock(M_STATE);
1222 
1223     setCurForm(EF);
1224 
1225     if (setupMenu) {
1226         setupDefMenu();
1227     }
1228     INFO2("[DS]: setEditfield done %d", _curForm);
1229 }
1230 
setPassField()1231 void setPassField()
1232 {
1233     logger(L_INF, "[DS]: setPassField");
1234 
1235     boolean_t setupMenu = (curForm() != EF);
1236 
1237     mutexLock(M_STATE);
1238 
1239     setEfCaption("Enter password");
1240     setEfLabel  ("Enter password");
1241     setEfText   ("");
1242     setEfText   ("");
1243     setEfPassword(BOOL_YES);
1244 
1245     mutexUnlock(M_STATE);
1246 
1247     setCurForm(EF);
1248 
1249     if (setupMenu) {
1250         setupDefMenu();
1251     }
1252 }
1253 
1254 
1255 ////////////////////////////////////////////////////////////////////////////////
1256 //
1257 //  Window Form
1258 //
1259 ////////////////////////////////////////////////////////////////////////////////
1260 
1261 //
1262 // Set(image,window,_image_file_name_)
1263 // Set(image,icon,_icon_name_,_image_file_name_) -- not supported
1264 // Set(image,show|close|cursor|nocursor|remove_all) -- only 1,2 are supported
1265 //
setImage(const char * cmd,const char * file)1266 void setImage(const char* cmd, const char* file)
1267 {
1268     INFO2("[DS]: setImage %s %s",cmd,file);
1269     boolean_t setupMenu = (curForm() != WM);
1270 
1271     if (!cmd) {
1272         return;
1273     }
1274     while (isspace(*cmd)) {
1275         ++cmd;
1276     }
1277 
1278 
1279     if (strncmp(cmd,"window",6) == 0) {
1280 
1281         logger(L_INF, "[DS]: setImage: window");
1282 
1283         setCurForm(WM);
1284 
1285         if (!file) {
1286             return;
1287         }
1288         while (isspace(*file)) {
1289             ++file;
1290         }
1291 
1292         mutexLock(M_STATE);
1293         if (_state.wf.window) {
1294              stringAssign(_state.wf.window, file);
1295         } else {
1296              _state.wf.window = stringNew(file);
1297         }
1298         mutexUnlock(M_STATE);
1299 
1300     } else if (strncmp(cmd,"show",4) == 0) {
1301         setCurForm(WM);
1302     } else if (strncmp(cmd,"close",5) == 0) {
1303         setCurForm(CF);
1304         setupDefMenu();
1305         return;
1306     } else {
1307         logger(L_INF, "[DS]: setImage: skip command");
1308         return;
1309     }
1310 
1311 
1312     if (setupMenu) {
1313         setupDefMenu();
1314     }
1315 }
1316 
wfImage()1317 const char* wfImage()
1318 {
1319     return (_state.wf.window ? _state.wf.window->str : NULL);
1320 }
1321 
1322 ////////////////////////////////////////////////////////////////////////////////
1323 //
1324 //  Params
1325 //
1326 ////////////////////////////////////////////////////////////////////////////////
1327 
setParams()1328 void setParams()
1329 {
1330     char *token = strtok(NULL,",");
1331     if (!token) {
1332         return;
1333     }
1334 
1335     while (token) {
1336 
1337         while (isspace(*token)) {
1338             ++token;
1339         }
1340 
1341         if (strncmp(token,"icon_padding",12) == 0) {
1342 
1343             char *token2 = strtok(NULL,",");
1344             if (!token2) {
1345                 return;
1346             }
1347 
1348             while (isspace(*token2)) {
1349                 ++token2;
1350             }
1351 
1352             _iconPadding = atoi(token2);
1353             if (_iconPadding <= 0) {
1354                 _iconPadding = 0;
1355             }
1356 
1357             INFO2("[DS]: Use icon padding %d", _iconPadding);
1358 
1359         } else if (strncmp(token,"icon_size",9) == 0) {
1360 
1361             char *token2 = strtok(NULL,",");
1362             if (!token2) {
1363                 return;
1364             }
1365 
1366             while (isspace(*token2)) {
1367                 ++token2;
1368             }
1369 
1370             int iconSizeScale = atoi(token2);
1371             if (iconSizeScale <= 0) {
1372                 iconSizeScale = 64;
1373             }
1374 
1375             if (iconSizeScale <= 32) {
1376                 _iconSize = 32;
1377             } else if (iconSizeScale <= 48) {
1378                 _iconSize = 48;
1379             } else if (iconSizeScale <= 64) {
1380                 _iconSize = 64;
1381             } else {
1382                 _iconSize = 128;
1383             }
1384 
1385             INFO2("[DS]: Use icon size %d", _iconSize);
1386 
1387         } else {
1388             ERROR2("[DS]: Skip Set(parameter, ...) %s", token);
1389         }
1390 
1391         token = strtok(NULL,",");
1392     }
1393 }
1394 
iconPadding()1395 int iconPadding()
1396 {
1397     return _iconPadding;
1398 }
1399 
iconSize()1400 int iconSize()
1401 {
1402     return _iconSize;
1403 }
1404 
updateStateSetFile(dMessage * dm)1405 static void updateStateSetFile(dMessage* dm)
1406 {
1407     DEBUG2("[DS]: updateStateSetFile %s", dm->file);
1408     if (strncmp(dm->value,"Set(cover,noname",16) == 0) {
1409         setCfCover(dm->file);
1410         xmlSetLayoutOk(BOOL_NO);  // Need to regenerate
1411     } else if (strncmp(dm->value,"Set(image",9) == 0) {
1412         setImage((dm->value + 10), dm->file);  // 10 = 9 + ","
1413     } else {
1414         ERROR2("[DS]: Unknown command file: %s", (const char*) dm->value);
1415     }
1416 }
1417 
updateStateSet(dMessage * dm)1418 static void updateStateSet(dMessage* dm)
1419 {
1420     const char* value = dm->value;
1421 
1422     DEBUG2("[DS]: updateState %d", (value ? (int) strlen(value) : -1));
1423 
1424     char * cmd = strdup(value);
1425 
1426     stripCommandEnding(cmd);
1427 
1428     if (strlen(cmd) < MAXMAXLEN) {
1429         INFO2("[DS]: parse %d %s", curForm(), cmd);
1430     } else {
1431         INFO2("[DS]: parse %d ... command too long ...", curForm());
1432     }
1433 
1434     char* token = strtok(cmd,",");
1435     while (isspace(*token)) {
1436         ++token;
1437     }
1438 
1439     if (strncmp(token,"Set(status",10) == 0) {
1440         setCfStatus(cmd+strlen("Set(status,"));
1441     } else if (strncmp(token,"Set(title",9) == 0) {
1442         setCfTitle(cmd+strlen("Set(title,"));
1443     } else if (strncmp(token,"Set(icons",9) == 0) {
1444         setIcons();
1445         xmlSetLayoutOk(BOOL_NO);  // Need to regenerate
1446     } else if (strncmp(token,"Set(hints",9) == 0) {
1447         setHints();
1448         xmlSetLayoutOk(BOOL_NO);  // Need to regenerate
1449     } else if (strncmp(token,"Set(font",8) == 0) {
1450         mutexLock(M_STATE);
1451         setFont(CF);
1452         mutexUnlock(M_STATE);
1453     } else if (strncmp(token,"Set(fg",6) == 0) {
1454         mutexLock(M_STATE);
1455         setFgBg(CF,BOOL_YES);
1456         mutexUnlock(M_STATE);
1457     } else if (strncmp(token,"Set(bg",6) == 0) {
1458         mutexLock(M_STATE);
1459         setFgBg(CF,BOOL_NO);
1460         mutexUnlock(M_STATE);
1461     } else if (strncmp(token,"Set(volume",10) == 0) {
1462         char* sz = strtok(NULL,",");
1463         mutexLock(M_STATE);
1464         setCfVolume(sz);
1465         mutexUnlock(M_STATE);
1466     } else if (strncmp(token,"Set(layout",10) == 0 ||
1467                strncmp(token,"Set(skin",8) == 0) {
1468         setSkin();
1469         xmlSetLayoutOk(BOOL_NO);  // Need to regenerate
1470     } else if (strncmp(token,"Set(cover",9) == 0) {
1471         char* subcmd = strtok(NULL,",");
1472         if (strncmp(subcmd,"by_name",7) == 0) {
1473             setCfNamedCover(subcmd+8);
1474             xmlSetLayoutOk(BOOL_NO);  // Need to regenerate
1475         } else if (strncmp(subcmd,"clear",5) == 0) {
1476             setCfNamedCover(NULL);
1477             xmlSetLayoutOk(BOOL_NO);  // Need to regenerate
1478         } else {
1479             ERROR2("[DS]: Improperly formed command Set(cover,...)");
1480         }
1481     } else if (strncmp(token,"Set(list",8) == 0) {
1482         setList(BOOL_NO);
1483     } else if (strncmp(token,"Set(iconlist",12) == 0) {
1484         setList(BOOL_YES);
1485     } else if (strncmp(token,"Set(text",8) == 0) {
1486         setText(BOOL_NO);
1487     } else if (strncmp(token,"Set(menu",8) == 0) {
1488         setMenu();
1489     } else if (strncmp(token,"Set(editfield",13) == 0) {
1490         setEditfield();
1491     } else if (strncmp(token,"Set(image",9) == 0) {
1492         ERROR2("[DS]: Improperly formed command Set(image,...)");
1493     } else if (strncmp(token,"Set(popup",9) == 0) {
1494         // ignore
1495     } else if (strncmp(token,"Set(disconnect",14) == 0) {
1496         // ignore
1497     } else if (strncmp(token,"Set(fullscreen",16) == 0) {
1498         // ignore
1499     } else if (strncmp(token,"Set(vibrate",11) == 0) {
1500         // ignore
1501     } else if (strncmp(token,"Set(parameter",13) == 0) {
1502         setParams();
1503     } else if (strncmp(token,"End",3) == 0) {
1504         // ignore
1505     } else {
1506         ERROR2("[DS]: Unknown command %s", token);
1507     }
1508 
1509     free(cmd);
1510 }
1511 
updateState(dMessage * dm)1512 void updateState(dMessage* dm)
1513 {
1514     if (dm->type == DM_SET) {
1515         updateStateSet(dm);
1516     }
1517     if (dm->type == DM_SETFILE) {
1518         updateStateSetFile(dm);
1519     }
1520 }
1521 
1522