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
));
227 /* We got one more widget. */
231 else if((strstr(line
, "symbolatom") != NULL
) &&
232 (strstr(line
, "pod_") != NULL
))
236 strtok_r(line
, " ", &saveptr
);
237 strtok_r(NULL
, " ", &saveptr
);
238 wg
->x
= atoi(strtok_r(NULL
, " ", &saveptr
)) * screen_multiplier
;
239 wg
->y
= atoi(strtok_r(NULL
, " ", &saveptr
)) * screen_multiplier
;
240 wg
->w
= 7 * atoi(strtok_r(NULL
, " ", &saveptr
)) *
242 wg
->h
= 16 * screen_multiplier
;
243 strtok_r(NULL
, " ", &saveptr
);
244 strtok_r(NULL
, " ", &saveptr
);
245 strtok_r(NULL
, " ", &saveptr
);
246 strtok_r(NULL
, " ", &saveptr
);
247 strncpy(wg
->name
, strtok_r(NULL
, " ", &saveptr
), sizeof(wg
->name
));
249 /* We got one more widget. */
253 else if((strstr(line
, "vsl") != NULL
) &&
254 (strstr(line
, "pod_") != NULL
))
258 strtok_r(line
, " ", &saveptr
);
259 strtok_r(NULL
, " ", &saveptr
);
260 wg
->x
= atoi(strtok_r(NULL
, " ", &saveptr
)) * screen_multiplier
;
261 wg
->y
= atoi(strtok_r(NULL
, " ", &saveptr
)) * screen_multiplier
;
262 strtok_r(NULL
, " ", &saveptr
);
263 wg
->w
= atoi(strtok_r(NULL
, " ", &saveptr
)) * screen_multiplier
;
264 wg
->h
= atoi(strtok_r(NULL
, " ", &saveptr
)) * screen_multiplier
;
265 wg
->min
= atoi(strtok_r(NULL
, " ", &saveptr
));
266 wg
->max
= atoi(strtok_r(NULL
, " ", &saveptr
));
267 strtok_r(NULL
, " ", &saveptr
);
268 strtok_r(NULL
, " ", &saveptr
);
269 strtok_r(NULL
, " ", &saveptr
);
270 strncpy(wg
->name
, strtok_r(NULL
, " ", &saveptr
), sizeof(wg
->name
));
272 /* We got one more widget. */
276 else if((strstr(line
, "hsl") != NULL
) &&
277 (strstr(line
, "pod_") != NULL
))
281 strtok_r(line
, " ", &saveptr
);
282 strtok_r(NULL
, " ", &saveptr
);
283 wg
->x
= atoi(strtok_r(NULL
, " ", &saveptr
)) * screen_multiplier
;
284 wg
->y
= atoi(strtok_r(NULL
, " ", &saveptr
)) * screen_multiplier
;
285 strtok_r(NULL
, " ", &saveptr
);
286 wg
->w
= atoi(strtok_r(NULL
, " ", &saveptr
)) * screen_multiplier
;
287 wg
->h
= atoi(strtok_r(NULL
, " ", &saveptr
)) * screen_multiplier
;
288 wg
->min
= atoi(strtok_r(NULL
, " ", &saveptr
));
289 wg
->max
= atoi(strtok_r(NULL
, " ", &saveptr
));
290 strtok_r(NULL
, " ", &saveptr
);
291 strtok_r(NULL
, " ", &saveptr
);
292 strtok_r(NULL
, " ", &saveptr
);
293 strncpy(wg
->name
, strtok_r(NULL
, " ", &saveptr
), sizeof(wg
->name
));
295 /* We got one more widget. */
299 else if((strstr(line
, "vradio") != NULL
) &&
300 (strstr(line
, "pod_") != NULL
))
304 strtok_r(line
, " ", &saveptr
);
305 strtok_r(NULL
, " ", &saveptr
);
306 wg
->x
= atoi(strtok_r(NULL
, " ", &saveptr
)) * screen_multiplier
;
307 wg
->y
= atoi(strtok_r(NULL
, " ", &saveptr
)) * screen_multiplier
;
308 strtok_r(NULL
, " ", &saveptr
);
309 wg
->w
= atoi(strtok_r(NULL
, " ", &saveptr
)) * screen_multiplier
;
310 strtok_r(NULL
, " ", &saveptr
);
311 strtok_r(NULL
, " ", &saveptr
);
313 wg
->max
= atoi(strtok_r(NULL
, " ", &saveptr
));
314 wg
->h
= wg
->w
* wg
->max
;
315 strtok_r(NULL
, " ", &saveptr
);
316 strncpy(wg
->name
, strtok_r(NULL
, " ", &saveptr
), sizeof(wg
->name
));
319 /* We got one more widget. */
323 else if((strstr(line
, "hradio") != NULL
) &&
324 (strstr(line
, "pod_") != NULL
))
328 strtok_r(line
, " ", &saveptr
);
329 strtok_r(NULL
, " ", &saveptr
);
330 wg
->x
= atoi(strtok_r(NULL
, " ", &saveptr
)) * screen_multiplier
;
331 wg
->y
= atoi(strtok_r(NULL
, " ", &saveptr
)) * screen_multiplier
;
332 strtok_r(NULL
, " ", &saveptr
);
333 wg
->h
= atoi(strtok_r(NULL
, " ", &saveptr
)) * screen_multiplier
;
334 strtok_r(NULL
, " ", &saveptr
);
335 strtok_r(NULL
, " ", &saveptr
);
337 wg
->max
= atoi(strtok_r(NULL
, " ", &saveptr
));
338 wg
->w
= wg
->h
* wg
->max
;
339 strtok_r(NULL
, " ", &saveptr
);
340 strncpy(wg
->name
, strtok_r(NULL
, " ", &saveptr
), sizeof(wg
->name
));
343 /* We got one more widget. */
347 else if((strstr(line
, "bng") != NULL
) &&
348 (strstr(line
, "pod_") != NULL
))
352 strtok_r(line
, " ", &saveptr
);
353 strtok_r(NULL
, " ", &saveptr
);
354 wg
->x
= atoi(strtok_r(NULL
, " ", &saveptr
)) * screen_multiplier
;
355 wg
->y
= atoi(strtok_r(NULL
, " ", &saveptr
)) * screen_multiplier
;
356 strtok_r(NULL
, " ", &saveptr
);
357 wg
->w
= atoi(strtok_r(NULL
, " ", &saveptr
)) * screen_multiplier
;
359 strtok_r(NULL
, " ", &saveptr
);
360 strtok_r(NULL
, " ", &saveptr
);
361 strtok_r(NULL
, " ", &saveptr
);
362 strtok_r(NULL
, " ", &saveptr
);
363 strncpy(wg
->name
, strtok_r(NULL
, " ", &saveptr
), sizeof(wg
->name
));
367 /* Clear timeout flag. */
370 /* We got one more widget. */
374 else if(strstr(line
, "text") != NULL
)
378 strtok_r(line
, " ", &saveptr
);
379 strtok_r(NULL
, " ", &saveptr
);
380 wg
->x
= atoi(strtok_r(NULL
, " ", &saveptr
)) * screen_multiplier
;
381 wg
->y
= atoi(strtok_r(NULL
, " ", &saveptr
)) * screen_multiplier
;
382 strncpy(wg
->name
, strtok_r(NULL
, " ", &saveptr
), sizeof(wg
->name
));
383 char* w
= strtok_r(NULL
, " ", &saveptr
);
387 strcat(wg
->name
, " ");
388 w
= strtok_r(NULL
, " ", &saveptr
);
390 /* Cut off unneeded characters (';' and '\n'). */
391 int namelen
= strlen(wg
->name
);
395 wg
->name
[namelen
-1] = '\0';
397 /* Cut the last semi-colon, if there is one. */
398 if(wg
->name
[namelen
-1] == ';')
399 wg
->name
[namelen
-1] = '\0';
403 /* We got one more widget. */
409 /* Close PD patch. */
412 /* Return amount of loaded widgets. */
416 /* Draw standard user interface. */
417 void pd_gui_draw_standard(void)
419 /* Draw main circle. */
420 rb
->lcd_set_foreground(fgcolor
);
421 fillcircle(LCD_WIDTH
/ 2,
423 2 * MIN(LCD_WIDTH
, LCD_HEIGHT
) / 5);
425 /* Draw center circle. */
426 rb
->lcd_set_foreground(action_on
? BTNCOLOR_DARK
: BTNCOLOR_LIGHT
);
427 fillcircle(LCD_WIDTH
/ 2,
429 MIN(LCD_WIDTH
, LCD_HEIGHT
) / 8);
431 /* Draw pressed buttons. */
433 fillcircle(LCD_WIDTH
/ 2,
435 MIN(LCD_WIDTH
, LCD_HEIGHT
) / 8);
438 fillcircle(LCD_WIDTH
/ 3,
440 MIN(LCD_WIDTH
, LCD_HEIGHT
) / 8);
443 fillcircle(2 * LCD_WIDTH
/ 3 + 1,
445 MIN(LCD_WIDTH
, LCD_HEIGHT
) / 8);
448 fillcircle(LCD_WIDTH
/ 2,
450 MIN(LCD_WIDTH
, LCD_HEIGHT
) / 8);
452 /* Restore foreground color. */
453 rb
->lcd_set_foreground(fgcolor
);
456 /* Draw custom user interface. */
457 void pd_gui_draw_custom(struct pd_widget
* wg
, unsigned int widgets
)
463 for(i
= 0; i
< widgets
; wg
++, i
++)
468 /* Clear area to (re-)draw. */
469 rb
->lcd_set_foreground(bgcolor
);
470 rb
->lcd_fillrect(wg
->x
, wg
->y
, wg
->w
, wg
->h
);
471 rb
->lcd_set_foreground(fgcolor
);
472 /* Draw border (rectangle). */
473 rb
->lcd_drawrect(wg
->x
, wg
->y
, wg
->w
, wg
->h
);
474 /* Draw button (circle), being filled depending on value. */
475 if(((int) wg
->value
) == 0) /* Button not pressed. */
476 drawcircle(wg
->x
+ wg
->w
/2,
479 else /* Button pressed. */
480 fillcircle(wg
->x
+ wg
->w
/2,
486 /* Clear area to (re-)draw. */
487 rb
->lcd_set_foreground(bgcolor
);
488 rb
->lcd_fillrect(wg
->x
, wg
->y
, wg
->w
, wg
->h
);
489 rb
->lcd_set_foreground(fgcolor
);
491 rb
->lcd_drawrect(wg
->x
, wg
->y
, wg
->w
, wg
->h
);
493 v
= ((float) wg
->h
/ (wg
->max
- wg
->min
)) *
494 (wg
->max
- wg
->value
);
495 rb
->lcd_fillrect(wg
->x
, wg
->y
+ v
, wg
->w
, 2);
499 /* Clear area to (re-)draw. */
500 rb
->lcd_set_foreground(bgcolor
);
501 rb
->lcd_fillrect(wg
->x
, wg
->y
, wg
->w
, wg
->h
);
502 rb
->lcd_set_foreground(fgcolor
);
504 rb
->lcd_drawrect(wg
->x
, wg
->y
, wg
->w
, wg
->h
);
506 v
= ((float) wg
->w
/ (wg
->max
- wg
->min
)) *
507 (wg
->max
- wg
->value
);
508 rb
->lcd_fillrect(wg
->x
+ wg
->w
- v
, wg
->y
, 2, wg
->h
);
512 /* Clear area to (re-)draw. */
513 rb
->lcd_set_foreground(bgcolor
);
514 rb
->lcd_fillrect(wg
->x
, wg
->y
, wg
->w
, wg
->h
);
515 rb
->lcd_set_foreground(fgcolor
);
516 for(j
= 0; j
< wg
->w
/ wg
->h
; j
++)
519 rb
->lcd_drawrect(wg
->x
+ wg
->h
* j
, wg
->y
, wg
->h
, wg
->h
);
520 /* If marked, draw button. */
521 if(((int) wg
->value
) == j
)
522 rb
->lcd_fillrect(wg
->x
+ wg
->h
* j
+ 2, wg
->y
+ 2,
523 wg
->h
- 4, wg
->h
- 4);
528 /* Clear area to (re-)draw. */
529 rb
->lcd_set_foreground(bgcolor
);
530 rb
->lcd_fillrect(wg
->x
, wg
->y
, wg
->w
, wg
->h
);
531 rb
->lcd_set_foreground(fgcolor
);
532 for(j
= 0; j
< wg
->h
/ wg
->w
; j
++)
535 rb
->lcd_drawrect(wg
->x
, wg
->y
+ wg
->w
* j
, wg
->w
, wg
->w
);
536 /* If marked, draw button. */
537 if(((int) wg
->value
) == j
)
538 rb
->lcd_fillrect(wg
->x
+ 2, wg
->y
+ wg
->w
* j
+ 2,
539 wg
->w
- 4, wg
->w
- 4);
545 wg
->x
+ wg
->w
- number_corner
,
547 rb
->lcd_drawline(wg
->x
+ wg
->w
- number_corner
,
550 wg
->y
+ number_corner
);
551 rb
->lcd_vline(wg
->x
+ wg
->w
,
552 wg
->y
+ number_corner
,
561 ftoan(wg
->value
, sbuf
, 12);
562 rb
->lcd_putsxy(wg
->x
+ 2, wg
->y
+ 2, sbuf
);
566 rb
->lcd_putsxy(wg
->x
+ 2, wg
->y
, wg
->name
);
576 void pd_gui_draw(struct pd_widget
* wg
, unsigned int widgets
)
580 pd_gui_draw_standard();
582 pd_gui_draw_custom(wg
, widgets
);
584 /* Update display. */
588 /* Parse buttons, if needed, send messages to the Pure Data code. */
589 bool pd_gui_parse_buttons(unsigned int widgets
)
591 static long last_bv
= 0;
592 long bv
= rb
->button_get(false);
594 /* Extract differences between current and previous button values. */
595 long diff_bv
= bv
^ last_bv
;
597 /* If no difference since the last button value, return here. */
601 /* Query whether we have to quit. */
604 /* No need to send the quitting message to Pure Data core,
605 as setting the quit flag ends threads and jumps
606 to the cleanup code. */
611 /* Check the action (shift, mode) button. */
612 if(diff_bv
& PDPOD_ACTION
)
614 if(bv
& PDPOD_ACTION
)
616 SEND_TO_CORE("b;\n");
628 /* Check play button. */
629 if(diff_bv
& PDPOD_PLAY
)
633 /* Action + play = pause. */
636 /* Switch paused state. */
638 SEND_TO_CORE(paused
? "p 1;\n" : "p 0;\n");
641 if(!action_on
&& !paused
)
642 SEND_TO_CORE("d 1;\n");
649 if(!action_on
&& !paused
)
650 SEND_TO_CORE("d 0;\n");
657 /* Check rewind (previous) button. */
658 if(diff_bv
& PDPOD_PREVIOUS
)
660 if(bv
& PDPOD_PREVIOUS
)
663 SEND_TO_CORE("w 1;\n");
671 SEND_TO_CORE("w 0;\n");
678 /* Check forward (next) button. */
679 if(diff_bv
& PDPOD_NEXT
)
684 SEND_TO_CORE("f 1;\n");
692 SEND_TO_CORE("f 0;\n");
699 /* Check menu (select) button. */
700 if(diff_bv
& PDPOD_MENU
)
704 if(!action_on
&& !paused
)
705 SEND_TO_CORE("m 1;\n");
712 if(!action_on
&& !paused
)
713 SEND_TO_CORE("m 0;\n");
720 /* Check scroll right (or up) button. */
721 if(diff_bv
& PDPOD_WHEELRIGHT
)
723 if(bv
& PDPOD_WHEELRIGHT
)
725 SEND_TO_CORE(action_on
? "r 10;\n" : "r 1;\n");
729 /* Check scroll left (or down) button. */
730 if(diff_bv
& PDPOD_WHEELLEFT
)
732 if(bv
& PDPOD_WHEELLEFT
)
734 SEND_TO_CORE(action_on
? "l 10;\n" : "l 1;\n");
738 /* Backup button value. */
741 /* GUI has to be updated. */
745 /* Emulate timer for widgets which use time-out. */
746 bool pd_gui_apply_timeouts(struct pd_widget
* wg
, unsigned int widgets
)
751 for(i
= 0; i
< widgets
; wg
++, i
++)
753 if(wg
->id
== PD_BANG
)
757 /* Decrement timeout value. */
760 /* If zero reached, clear value. */
773 /* Parse and apply message from the Pure Data core. */
774 void pd_gui_parse_message(struct datagram
* dg
,
775 struct pd_widget
* wg
,
776 unsigned int widgets
)
781 char* argument
= NULL
;
784 object
= strtok_r(dg
->data
, " ", &saveptr
);
785 argument
= strtok_r(NULL
, " ;\n", &saveptr
);
788 argvalue
= atof(argument
);
790 for(i
= 0; i
< widgets
; wg
++, i
++)
792 if(strncmp(object
, wg
->name
, strlen(wg
->name
)) == 0)
794 /* If object not a number, set boundaries. */
795 if(wg
->id
!= PD_NUMBER
)
797 if(argvalue
> wg
->max
)
799 else if(argvalue
< wg
->min
)
804 if(wg
->id
== PD_BANG
)
807 wg
->timeout
= HZ
/ 10;
811 wg
->value
= argvalue
;