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