1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
10 * Copyright (C) 2009 Wincent Balin
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
20 ****************************************************************************/
28 #define BTNCOLOR_DARK (LCD_DARKGRAY)
29 #define BTNCOLOR_LIGHT (LCD_LIGHTGRAY)
31 /* Variables in the main code. */
32 extern char* filename
;
35 /* Screen multiplier. */
36 static float screen_multiplier
;
38 /* Back- and foreground colors. */
39 static unsigned bgcolor
;
40 static unsigned fgcolor
;
42 /* Displacement of the slanted corner in the contour of the number widget. */
43 static int number_corner
;
47 static bool previous_on
;
50 static bool action_on
;
56 /* Draw circle using midpoint circle algorithm.
57 Adapted from http://en.wikipedia.org/wiki/Midpoint_circle_algorithm. */
58 void drawcircle(int x
, int y
, int r
)
66 /* Draw outer points. */
67 rb
->lcd_drawpixel(x
, y
+ r
);
68 rb
->lcd_drawpixel(x
, y
+ r
);
69 rb
->lcd_drawpixel(x
+ r
, y
);
70 rb
->lcd_drawpixel(x
- r
, y
);
72 /* Calculate coordinates of points in one octant. */
75 /* ddfx == 2 * xp + 1;
77 f == xp*xp + yp*yp - r*r + 2*xp - yp + 1; */
89 /* Draw pixels in all octants. */
90 rb
->lcd_drawpixel(x
+ xp
, y
+ yp
);
91 rb
->lcd_drawpixel(x
+ xp
, y
- yp
);
92 rb
->lcd_drawpixel(x
- xp
, y
+ yp
);
93 rb
->lcd_drawpixel(x
- xp
, y
- yp
);
94 rb
->lcd_drawpixel(x
+ yp
, y
+ xp
);
95 rb
->lcd_drawpixel(x
+ yp
, y
- xp
);
96 rb
->lcd_drawpixel(x
- yp
, y
+ xp
);
97 rb
->lcd_drawpixel(x
- yp
, y
- xp
);
102 void fillcircle(int x
, int y
, int r
)
110 /* Draw outer points. */
111 rb
->lcd_drawpixel(x
, y
+ r
);
112 rb
->lcd_drawpixel(x
, y
+ r
);
113 rb
->lcd_drawpixel(x
+ r
, y
);
114 rb
->lcd_drawpixel(x
- r
, y
);
116 /* Calculate coordinates of points in one octant. */
119 /* ddfx == 2 * xp + 1;
121 f == xp*xp + yp*yp - r*r + 2*xp - yp + 1; */
133 /* Fill circle with horizontal lines. */
134 rb
->lcd_hline(x
- xp
, x
+ xp
, y
- yp
);
135 rb
->lcd_hline(x
- xp
, x
+ xp
, y
+ yp
);
136 rb
->lcd_hline(x
- yp
, x
+ yp
, y
- xp
);
137 rb
->lcd_hline(x
- yp
, x
+ yp
, y
+ xp
);
140 /* Draw last horizontal line (central one). */
141 rb
->lcd_hline(x
- r
, x
+ r
, y
);
144 /* Initialize GUI. */
145 void pd_gui_init(void)
147 /* Reset button flags. */
154 /* Unpause Pure Data. */
157 /* Calculate dimension factors. */
158 screen_multiplier
= ((float) LCD_WIDTH
) / 160.0f
;
159 number_corner
= 5 * screen_multiplier
;
161 /* Get back- and foreground colors. */
162 bgcolor
= rb
->lcd_get_background();
163 fgcolor
= rb
->lcd_get_foreground();
165 /* Clear background. */
166 rb
->lcd_clear_display();
168 /* Draw background of appropriate color. */
169 rb
->lcd_set_foreground(bgcolor
);
170 rb
->lcd_fillrect(0, 0, LCD_WIDTH
, LCD_HEIGHT
);
171 rb
->lcd_set_foreground(fgcolor
);
173 /* Update display. */
178 unsigned int pd_gui_load_patch(struct pd_widget
* wg
, unsigned int max_widgets
)
183 unsigned int widgets
= 0;
186 fd
= open(filename
, O_RDONLY
);
188 /* Check for I/O error. */
191 /* Show error message and make plug-in quit. */
192 rb
->splash(HZ
, "Error opening .pd file!");
197 /* Read lines from PD file. */
198 while(rb
->read_line(fd
, line
, sizeof(line
)) > 0)
200 /* Check whether we got too many widgets. */
201 if(widgets
>= max_widgets
)
203 rb
->splash(HZ
, "Too many widgets!");
208 /* Search for key strings in the line. */
209 if((strstr(line
, "floatatom") != NULL
) &&
210 (strstr(line
, "pod_") != NULL
))
214 strtok_r(line
, " ", &saveptr
);
215 strtok_r(NULL
, " ", &saveptr
);
216 wg
->x
= atoi(strtok_r(NULL
, " ", &saveptr
)) * screen_multiplier
;
217 wg
->y
= atoi(strtok_r(NULL
, " ", &saveptr
)) * screen_multiplier
;
218 wg
->w
= 7 * atoi(strtok_r(NULL
, " ", &saveptr
)) *
220 wg
->h
= 16 * screen_multiplier
;
221 strtok_r(NULL
, " ", &saveptr
);
222 strtok_r(NULL
, " ", &saveptr
);
223 strtok_r(NULL
, " ", &saveptr
);
224 strtok_r(NULL
, " ", &saveptr
);
225 strncpy(wg
->name
, strtok_r(NULL
, " ", &saveptr
), sizeof(wg
->name
));
230 /* We got one more widget. */
234 else if((strstr(line
, "symbolatom") != NULL
) &&
235 (strstr(line
, "pod_") != NULL
))
239 strtok_r(line
, " ", &saveptr
);
240 strtok_r(NULL
, " ", &saveptr
);
241 wg
->x
= atoi(strtok_r(NULL
, " ", &saveptr
)) * screen_multiplier
;
242 wg
->y
= atoi(strtok_r(NULL
, " ", &saveptr
)) * screen_multiplier
;
243 wg
->w
= 7 * atoi(strtok_r(NULL
, " ", &saveptr
)) *
245 wg
->h
= 16 * screen_multiplier
;
246 strtok_r(NULL
, " ", &saveptr
);
247 strtok_r(NULL
, " ", &saveptr
);
248 strtok_r(NULL
, " ", &saveptr
);
249 strtok_r(NULL
, " ", &saveptr
);
250 strncpy(wg
->name
, strtok_r(NULL
, " ", &saveptr
), sizeof(wg
->name
));
255 /* We got one more widget. */
259 else if((strstr(line
, "vsl") != NULL
) &&
260 (strstr(line
, "pod_") != NULL
))
264 strtok_r(line
, " ", &saveptr
);
265 strtok_r(NULL
, " ", &saveptr
);
266 wg
->x
= atoi(strtok_r(NULL
, " ", &saveptr
)) * screen_multiplier
;
267 wg
->y
= atoi(strtok_r(NULL
, " ", &saveptr
)) * screen_multiplier
;
268 strtok_r(NULL
, " ", &saveptr
);
269 wg
->w
= atoi(strtok_r(NULL
, " ", &saveptr
)) * screen_multiplier
;
270 wg
->h
= atoi(strtok_r(NULL
, " ", &saveptr
)) * screen_multiplier
;
271 wg
->min
= atoi(strtok_r(NULL
, " ", &saveptr
));
272 wg
->max
= atoi(strtok_r(NULL
, " ", &saveptr
));
273 strtok_r(NULL
, " ", &saveptr
);
274 strtok_r(NULL
, " ", &saveptr
);
275 strtok_r(NULL
, " ", &saveptr
);
276 strncpy(wg
->name
, strtok_r(NULL
, " ", &saveptr
), sizeof(wg
->name
));
281 /* We got one more widget. */
285 else if((strstr(line
, "hsl") != NULL
) &&
286 (strstr(line
, "pod_") != NULL
))
290 strtok_r(line
, " ", &saveptr
);
291 strtok_r(NULL
, " ", &saveptr
);
292 wg
->x
= atoi(strtok_r(NULL
, " ", &saveptr
)) * screen_multiplier
;
293 wg
->y
= atoi(strtok_r(NULL
, " ", &saveptr
)) * screen_multiplier
;
294 strtok_r(NULL
, " ", &saveptr
);
295 wg
->w
= atoi(strtok_r(NULL
, " ", &saveptr
)) * screen_multiplier
;
296 wg
->h
= atoi(strtok_r(NULL
, " ", &saveptr
)) * screen_multiplier
;
297 wg
->min
= atoi(strtok_r(NULL
, " ", &saveptr
));
298 wg
->max
= atoi(strtok_r(NULL
, " ", &saveptr
));
299 strtok_r(NULL
, " ", &saveptr
);
300 strtok_r(NULL
, " ", &saveptr
);
301 strtok_r(NULL
, " ", &saveptr
);
302 strncpy(wg
->name
, strtok_r(NULL
, " ", &saveptr
), sizeof(wg
->name
));
307 /* We got one more widget. */
311 else if((strstr(line
, "vradio") != NULL
) &&
312 (strstr(line
, "pod_") != NULL
))
316 strtok_r(line
, " ", &saveptr
);
317 strtok_r(NULL
, " ", &saveptr
);
318 wg
->x
= atoi(strtok_r(NULL
, " ", &saveptr
)) * screen_multiplier
;
319 wg
->y
= atoi(strtok_r(NULL
, " ", &saveptr
)) * screen_multiplier
;
320 strtok_r(NULL
, " ", &saveptr
);
321 wg
->w
= atoi(strtok_r(NULL
, " ", &saveptr
)) * screen_multiplier
;
322 strtok_r(NULL
, " ", &saveptr
);
323 strtok_r(NULL
, " ", &saveptr
);
325 wg
->max
= atoi(strtok_r(NULL
, " ", &saveptr
));
326 wg
->h
= wg
->w
* wg
->max
;
327 strtok_r(NULL
, " ", &saveptr
);
328 strncpy(wg
->name
, strtok_r(NULL
, " ", &saveptr
), sizeof(wg
->name
));
334 /* We got one more widget. */
338 else if((strstr(line
, "hradio") != NULL
) &&
339 (strstr(line
, "pod_") != NULL
))
343 strtok_r(line
, " ", &saveptr
);
344 strtok_r(NULL
, " ", &saveptr
);
345 wg
->x
= atoi(strtok_r(NULL
, " ", &saveptr
)) * screen_multiplier
;
346 wg
->y
= atoi(strtok_r(NULL
, " ", &saveptr
)) * screen_multiplier
;
347 strtok_r(NULL
, " ", &saveptr
);
348 wg
->h
= atoi(strtok_r(NULL
, " ", &saveptr
)) * screen_multiplier
;
349 strtok_r(NULL
, " ", &saveptr
);
350 strtok_r(NULL
, " ", &saveptr
);
352 wg
->max
= atoi(strtok_r(NULL
, " ", &saveptr
));
353 wg
->w
= wg
->h
* wg
->max
;
354 strtok_r(NULL
, " ", &saveptr
);
355 strncpy(wg
->name
, strtok_r(NULL
, " ", &saveptr
), sizeof(wg
->name
));
361 /* We got one more widget. */
365 else if((strstr(line
, "bng") != NULL
) &&
366 (strstr(line
, "pod_") != NULL
))
370 strtok_r(line
, " ", &saveptr
);
371 strtok_r(NULL
, " ", &saveptr
);
372 wg
->x
= atoi(strtok_r(NULL
, " ", &saveptr
)) * screen_multiplier
;
373 wg
->y
= atoi(strtok_r(NULL
, " ", &saveptr
)) * screen_multiplier
;
374 strtok_r(NULL
, " ", &saveptr
);
375 wg
->w
= atoi(strtok_r(NULL
, " ", &saveptr
)) * screen_multiplier
;
377 strtok_r(NULL
, " ", &saveptr
);
378 strtok_r(NULL
, " ", &saveptr
);
379 strtok_r(NULL
, " ", &saveptr
);
380 strtok_r(NULL
, " ", &saveptr
);
381 strncpy(wg
->name
, strtok_r(NULL
, " ", &saveptr
), sizeof(wg
->name
));
388 /* Clear timeout flag. */
391 /* We got one more widget. */
395 else if(strstr(line
, "text") != NULL
)
399 strtok_r(line
, " ", &saveptr
);
400 strtok_r(NULL
, " ", &saveptr
);
401 wg
->x
= atoi(strtok_r(NULL
, " ", &saveptr
)) * screen_multiplier
;
402 wg
->y
= atoi(strtok_r(NULL
, " ", &saveptr
)) * screen_multiplier
;
403 strncpy(wg
->name
, strtok_r(NULL
, " ", &saveptr
), sizeof(wg
->name
));
404 char* w
= strtok_r(NULL
, " ", &saveptr
);
408 strcat(wg
->name
, " ");
409 w
= strtok_r(NULL
, " ", &saveptr
);
411 /* Cut off unneeded characters (';' and '\n'). */
412 int namelen
= strlen(wg
->name
);
416 wg
->name
[namelen
-1] = '\0';
418 /* Cut the last semi-colon, if there is one. */
419 if(wg
->name
[namelen
-1] == ';')
420 wg
->name
[namelen
-1] = '\0';
424 /* We got one more widget. */
430 /* Close PD patch. */
433 /* Return amount of loaded widgets. */
437 /* Draw standard user interface. */
438 void pd_gui_draw_standard(void)
440 /* Draw main circle. */
441 rb
->lcd_set_foreground(fgcolor
);
442 fillcircle(LCD_WIDTH
/ 2,
444 2 * MIN(LCD_WIDTH
, LCD_HEIGHT
) / 5);
446 /* Draw center circle. */
447 rb
->lcd_set_foreground(action_on
? BTNCOLOR_DARK
: BTNCOLOR_LIGHT
);
448 fillcircle(LCD_WIDTH
/ 2,
450 MIN(LCD_WIDTH
, LCD_HEIGHT
) / 8);
452 /* Draw pressed buttons. */
454 fillcircle(LCD_WIDTH
/ 2,
456 MIN(LCD_WIDTH
, LCD_HEIGHT
) / 8);
459 fillcircle(LCD_WIDTH
/ 3,
461 MIN(LCD_WIDTH
, LCD_HEIGHT
) / 8);
464 fillcircle(2 * LCD_WIDTH
/ 3 + 1,
466 MIN(LCD_WIDTH
, LCD_HEIGHT
) / 8);
469 fillcircle(LCD_WIDTH
/ 2,
471 MIN(LCD_WIDTH
, LCD_HEIGHT
) / 8);
473 /* Restore foreground color. */
474 rb
->lcd_set_foreground(fgcolor
);
477 /* Draw custom user interface. */
478 void pd_gui_draw_custom(struct pd_widget
* wg
, unsigned int widgets
)
484 for(i
= 0; i
< widgets
; wg
++, i
++)
489 /* Clear area to (re-)draw. */
490 rb
->lcd_set_foreground(bgcolor
);
491 rb
->lcd_fillrect(wg
->x
, wg
->y
, wg
->w
, wg
->h
);
492 rb
->lcd_set_foreground(fgcolor
);
493 /* Draw border (rectangle). */
494 rb
->lcd_drawrect(wg
->x
, wg
->y
, wg
->w
, wg
->h
);
495 /* Draw button (circle), being filled depending on value. */
496 if(((int) wg
->value
) == 0) /* Button not pressed. */
497 drawcircle(wg
->x
+ wg
->w
/2,
500 else /* Button pressed. */
501 fillcircle(wg
->x
+ wg
->w
/2,
507 /* Clear area to (re-)draw. */
508 rb
->lcd_set_foreground(bgcolor
);
509 rb
->lcd_fillrect(wg
->x
, wg
->y
, wg
->w
, wg
->h
);
510 rb
->lcd_set_foreground(fgcolor
);
512 rb
->lcd_drawrect(wg
->x
, wg
->y
, wg
->w
, wg
->h
);
514 v
= ((float) wg
->h
/ (wg
->max
- wg
->min
)) *
515 (wg
->max
- wg
->value
);
516 rb
->lcd_fillrect(wg
->x
, wg
->y
+ v
, wg
->w
, 2);
520 /* Clear area to (re-)draw. */
521 rb
->lcd_set_foreground(bgcolor
);
522 rb
->lcd_fillrect(wg
->x
, wg
->y
, wg
->w
, wg
->h
);
523 rb
->lcd_set_foreground(fgcolor
);
525 rb
->lcd_drawrect(wg
->x
, wg
->y
, wg
->w
, wg
->h
);
527 v
= ((float) wg
->w
/ (wg
->max
- wg
->min
)) *
528 (wg
->max
- wg
->value
);
529 rb
->lcd_fillrect(wg
->x
+ wg
->w
- v
, wg
->y
, 2, wg
->h
);
533 /* Clear area to (re-)draw. */
534 rb
->lcd_set_foreground(bgcolor
);
535 rb
->lcd_fillrect(wg
->x
, wg
->y
, wg
->w
, wg
->h
);
536 rb
->lcd_set_foreground(fgcolor
);
537 for(j
= 0; j
< wg
->w
/ wg
->h
; j
++)
540 rb
->lcd_drawrect(wg
->x
+ wg
->h
* j
, wg
->y
, wg
->h
, wg
->h
);
541 /* If marked, draw button. */
542 if(((int) wg
->value
) == j
)
543 rb
->lcd_fillrect(wg
->x
+ wg
->h
* j
+ 2, wg
->y
+ 2,
544 wg
->h
- 4, wg
->h
- 4);
549 /* Clear area to (re-)draw. */
550 rb
->lcd_set_foreground(bgcolor
);
551 rb
->lcd_fillrect(wg
->x
, wg
->y
, wg
->w
, wg
->h
);
552 rb
->lcd_set_foreground(fgcolor
);
553 for(j
= 0; j
< wg
->h
/ wg
->w
; j
++)
556 rb
->lcd_drawrect(wg
->x
, wg
->y
+ wg
->w
* j
, wg
->w
, wg
->w
);
557 /* If marked, draw button. */
558 if(((int) wg
->value
) == j
)
559 rb
->lcd_fillrect(wg
->x
+ 2, wg
->y
+ wg
->w
* j
+ 2,
560 wg
->w
- 4, wg
->w
- 4);
566 wg
->x
+ wg
->w
- number_corner
,
568 rb
->lcd_drawline(wg
->x
+ wg
->w
- number_corner
,
571 wg
->y
+ number_corner
);
572 rb
->lcd_vline(wg
->x
+ wg
->w
,
573 wg
->y
+ number_corner
,
582 ftoan(wg
->value
, sbuf
, 8);
583 rb
->lcd_putsxy(wg
->x
+ 2, wg
->y
+ 2, sbuf
);
587 rb
->lcd_putsxy(wg
->x
+ 2, wg
->y
, wg
->name
);
597 void pd_gui_draw(struct pd_widget
* wg
, unsigned int widgets
)
601 pd_gui_draw_standard();
603 pd_gui_draw_custom(wg
, widgets
);
605 /* Update display. */
609 /* Parse buttons, if needed, send messages to the Pure Data code. */
610 bool pd_gui_parse_buttons(unsigned int widgets
)
612 static long last_bv
= 0;
613 long bv
= rb
->button_get(false);
615 /* Extract differences between current and previous button values. */
616 long diff_bv
= bv
^ last_bv
;
618 /* If no difference since the last button value, return here. */
622 /* Query whether we have to quit. */
625 /* No need to send the quitting message to Pure Data core,
626 as setting the quit flag ends threads and jumps
627 to the cleanup code. */
632 /* Check the action (shift, mode) button. */
633 if(diff_bv
& PDPOD_ACTION
)
635 if(bv
& PDPOD_ACTION
)
637 SEND_TO_CORE("b;\n");
649 /* Check play button. */
650 if(diff_bv
& PDPOD_PLAY
)
654 /* Action + play = pause. */
657 /* Switch paused state. */
659 SEND_TO_CORE(paused
? "p 1;\n" : "p 0;\n");
662 if(!action_on
&& !paused
)
663 SEND_TO_CORE("d 1;\n");
670 if(!action_on
&& !paused
)
671 SEND_TO_CORE("d 0;\n");
678 /* Check rewind (previous) button. */
679 if(diff_bv
& PDPOD_PREVIOUS
)
681 if(bv
& PDPOD_PREVIOUS
)
684 SEND_TO_CORE("w 1;\n");
692 SEND_TO_CORE("w 0;\n");
699 /* Check forward (next) button. */
700 if(diff_bv
& PDPOD_NEXT
)
705 SEND_TO_CORE("f 1;\n");
713 SEND_TO_CORE("f 0;\n");
720 /* Check menu (select) button. */
721 if(diff_bv
& PDPOD_MENU
)
725 if(!action_on
&& !paused
)
726 SEND_TO_CORE("m 1;\n");
733 if(!action_on
&& !paused
)
734 SEND_TO_CORE("m 0;\n");
741 /* Check scroll right (or up) button. */
742 if(diff_bv
& PDPOD_WHEELRIGHT
)
744 if(bv
& PDPOD_WHEELRIGHT
)
746 SEND_TO_CORE(action_on
? "r 10;\n" : "r 1;\n");
750 /* Check scroll left (or down) button. */
751 if(diff_bv
& PDPOD_WHEELLEFT
)
753 if(bv
& PDPOD_WHEELLEFT
)
755 SEND_TO_CORE(action_on
? "l 10;\n" : "l 1;\n");
759 /* Backup button value. */
762 /* GUI has to be updated. */
766 /* Emulate timer for widgets which use time-out. */
767 bool pd_gui_apply_timeouts(struct pd_widget
* wg
, unsigned int widgets
)
772 for(i
= 0; i
< widgets
; wg
++, i
++)
774 if(wg
->id
== PD_BANG
)
778 /* Decrement timeout value. */
781 /* If zero reached, clear value. */
794 /* Parse and apply message from the Pure Data core. */
795 void pd_gui_parse_message(struct datagram
* dg
,
796 struct pd_widget
* wg
,
797 unsigned int widgets
)
802 char* argument
= NULL
;
805 object
= strtok_r(dg
->data
, " ", &saveptr
);
806 argument
= strtok_r(NULL
, " ;\n", &saveptr
);
809 argvalue
= atof(argument
);
811 for(i
= 0; i
< widgets
; wg
++, i
++)
813 if(strncmp(object
, wg
->name
, strlen(wg
->name
)) == 0)
815 /* If object not a number, set boundaries. */
816 if(wg
->id
!= PD_NUMBER
)
818 if(argvalue
> wg
->max
)
820 else if(argvalue
< wg
->min
)
825 if(wg
->id
== PD_BANG
)
828 wg
->timeout
= HZ
/ 10;
832 wg
->value
= argvalue
;