PDBox: Added GUI.


git-svn-id: svn://svn.rockbox.org/rockbox/trunk@22147 a1c6a512-1295-4272-9138-f99709370657
diff --git a/apps/plugins/pdbox/SOURCES b/apps/plugins/pdbox/SOURCES
index 04cff3c..22b093f 100644
--- a/apps/plugins/pdbox/SOURCES
+++ b/apps/plugins/pdbox/SOURCES
@@ -1,6 +1,7 @@
 pdbox.c
 pdbox-net.c
 pdbox-func.c
+pdbox-gui.c
 
 TLSF-2.4.4/src/tlsf.c
 /*
diff --git a/apps/plugins/pdbox/pdbox-gui.c b/apps/plugins/pdbox/pdbox-gui.c
new file mode 100644
index 0000000..a03e97c
--- /dev/null
+++ b/apps/plugins/pdbox/pdbox-gui.c
@@ -0,0 +1,809 @@
+/***************************************************************************
+ *             __________               __   ___.
+ *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
+ *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
+ *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
+ *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
+ *                     \/            \/     \/    \/            \/
+ * $Id$
+ *
+ * Copyright (C) 2009 Wincent Balin
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#include "plugin.h"
+#include "pdbox.h"
+
+#include "lib/xlcd.h"
+
+/* Declare back- and foreground colors. */
+#define BGCOLOR (LCD_BLACK)
+#define FGCOLOR (LCD_WHITE)
+/* Button colors. */
+#define BTNCOLOR_DARK (LCD_DARKGRAY)
+#define BTNCOLOR_LIGHT (LCD_LIGHTGRAY)
+
+/* Variables in the main code. */
+extern char* filename;
+extern bool quit;
+
+/* Screen multiplier. */
+static float screen_multiplier;
+
+/* Displacement of the slanted corner in the contour of the number widget. */
+static int number_corner;
+
+/* Button flags. */
+static bool play_on;
+static bool previous_on;
+static bool next_on;
+static bool menu_on;
+static bool action_on;
+
+/* Pause flag. */
+static bool paused;
+
+
+/* Draw circle using midpoint circle algorithm.
+   Adapted from http://en.wikipedia.org/wiki/Midpoint_circle_algorithm. */
+void drawcircle(int x, int y, int r)
+{
+    int f = 1 - r;
+    int ddfx = 1;
+    int ddfy = -2 * r;
+    int xp = 0;
+    int yp = r;
+
+    /* Draw outer points. */
+    rb->lcd_drawpixel(x, y + r);
+    rb->lcd_drawpixel(x, y + r);
+    rb->lcd_drawpixel(x + r, y);
+    rb->lcd_drawpixel(x - r, y);
+
+    /* Calculate coordinates of points in one octant. */
+    while(xp < yp)
+    {
+        /* ddfx == 2 * xp + 1;
+           ddfy == -2 * yp;
+           f == xp*xp + yp*yp - r*r + 2*xp - yp + 1; */
+        if(f >= 0)
+        {
+            yp--;
+            ddfy += 2;
+            f += ddfy;
+        }
+
+        xp++;
+        ddfx += 2;
+        f += ddfx;
+
+        /* Draw pixels in all octants. */
+        rb->lcd_drawpixel(x + xp, y + yp);
+        rb->lcd_drawpixel(x + xp, y - yp);
+        rb->lcd_drawpixel(x - xp, y + yp);
+        rb->lcd_drawpixel(x - xp, y - yp);
+        rb->lcd_drawpixel(x + yp, y + xp);
+        rb->lcd_drawpixel(x + yp, y - xp);
+        rb->lcd_drawpixel(x - yp, y + xp);
+        rb->lcd_drawpixel(x - yp, y - xp);
+    }
+}
+
+/* Fill circle. */
+void fillcircle(int x, int y, int r)
+{
+    int f = 1 - r;
+    int ddfx = 1;
+    int ddfy = -2 * r;
+    int xp = 0;
+    int yp = r;
+
+    /* Draw outer points. */
+    rb->lcd_drawpixel(x, y + r);
+    rb->lcd_drawpixel(x, y + r);
+    rb->lcd_drawpixel(x + r, y);
+    rb->lcd_drawpixel(x - r, y);
+
+    /* Calculate coordinates of points in one octant. */
+    while(xp < yp)
+    {
+        /* ddfx == 2 * xp + 1;
+           ddfy == -2 * yp;
+           f == xp*xp + yp*yp - r*r + 2*xp - yp + 1; */
+        if(f >= 0)
+        {
+            yp--;
+            ddfy += 2;
+            f += ddfy;
+        }
+
+        xp++;
+        ddfx += 2;
+        f += ddfx;
+
+        /* Fill circle with horizontal lines. */
+        rb->lcd_hline(x - xp, x + xp, y - yp);
+        rb->lcd_hline(x - xp, x + xp, y + yp);
+        rb->lcd_hline(x - yp, x + yp, y - xp);
+        rb->lcd_hline(x - yp, x + yp, y + xp);
+    }
+
+    /* Draw last horizontal line (central one). */
+    rb->lcd_hline(x - r, x + r, y);
+}
+
+/* Initialize GUI. */
+void pd_gui_init(void)
+{
+    /* Reset button flags. */
+    play_on = false;
+    previous_on = false;
+    next_on = false;
+    menu_on = false;
+    action_on = false;
+
+    /* Unpause Pure Data. */
+    paused = false;
+
+    /* Calculate dimension factors. */
+    screen_multiplier = ((float) LCD_WIDTH) / 160.0f;
+    number_corner = 5 * screen_multiplier;
+
+    /* Set back- and foreground color. */
+    rb->lcd_set_background(BGCOLOR);
+    rb->lcd_set_foreground(FGCOLOR);
+
+    /* Clear background. */
+    rb->lcd_clear_display();
+
+    /* Update display. */
+    rb->lcd_update();
+}
+
+/* Load PD patch. */
+unsigned int pd_gui_load_patch(struct pd_widget* wg, unsigned int max_widgets)
+{
+    int fd;
+    char line[100];
+    char* saveptr;
+    unsigned int widgets = 0;
+
+    /* Open PD patch. */
+    fd = open(filename, O_RDONLY);
+
+    /* Check for I/O error. */
+    if(!fd)
+    {
+        /* Show error message and make plug-in quit. */
+        rb->splash(HZ, "Error opening .pd file!");
+        quit = true;
+        return 0;
+    }
+
+    /* Read lines from PD file. */
+    while(rb->read_line(fd, line, sizeof(line)) > 0)
+    {
+        /* Check whether we got too many widgets. */
+        if(widgets >= max_widgets)
+        {
+            rb->splash(HZ, "Too many widgets!");
+            quit = true;
+            return 0;
+        }
+
+        /* Search for key strings in the line. */
+        if((strstr(line, "floatatom") != NULL) &&
+           (strstr(line, "pod_") != NULL))
+        {
+            wg->id = PD_NUMBER;
+
+            strtok_r(line, " ", &saveptr);
+            strtok_r(NULL, " ", &saveptr);
+            wg->x = atoi(strtok_r(NULL, " ", &saveptr)) * screen_multiplier;
+            wg->y = atoi(strtok_r(NULL, " ", &saveptr)) * screen_multiplier;
+            wg->w = 7 * atoi(strtok_r(NULL, " ", &saveptr)) *
+                                                          screen_multiplier;
+            wg->h = 16 * screen_multiplier;
+            strtok_r(NULL, " ", &saveptr);
+            strtok_r(NULL, " ", &saveptr);
+            strtok_r(NULL, " ", &saveptr);
+            strtok_r(NULL, " ", &saveptr);
+            strncpy(wg->name, strtok_r(NULL, " ", &saveptr), sizeof(wg->name));
+
+            /* We got one more widget. */
+            wg++;
+            widgets++;
+        }
+        else if((strstr(line, "symbolatom") != NULL) &&
+                (strstr(line, "pod_") != NULL))
+        {
+            wg->id = PD_SYMBOL;
+
+            strtok_r(line, " ", &saveptr);
+            strtok_r(NULL, " ", &saveptr);
+            wg->x = atoi(strtok_r(NULL, " ", &saveptr)) * screen_multiplier;
+            wg->y = atoi(strtok_r(NULL, " ", &saveptr)) * screen_multiplier;
+            wg->w = 7 * atoi(strtok_r(NULL, " ", &saveptr)) *
+                                                          screen_multiplier;
+            wg->h = 16 * screen_multiplier;
+            strtok_r(NULL, " ", &saveptr);
+            strtok_r(NULL, " ", &saveptr);
+            strtok_r(NULL, " ", &saveptr);
+            strtok_r(NULL, " ", &saveptr);
+            strncpy(wg->name, strtok_r(NULL, " ", &saveptr), sizeof(wg->name));
+
+            /* We got one more widget. */
+            wg++;
+            widgets++;
+        }
+        else if((strstr(line, "vsl") != NULL) &&
+                (strstr(line, "pod_") != NULL))
+        {
+            wg->id = PD_VSLIDER;
+
+            strtok_r(line, " ", &saveptr);
+            strtok_r(NULL, " ", &saveptr);
+            wg->x = atoi(strtok_r(NULL, " ", &saveptr)) * screen_multiplier;
+            wg->y = atoi(strtok_r(NULL, " ", &saveptr)) * screen_multiplier;
+            strtok_r(NULL, " ", &saveptr);
+            wg->w = atoi(strtok_r(NULL, " ", &saveptr)) * screen_multiplier;
+            wg->h = atoi(strtok_r(NULL, " ", &saveptr)) * screen_multiplier;
+            wg->min = atoi(strtok_r(NULL, " ", &saveptr));
+            wg->max = atoi(strtok_r(NULL, " ", &saveptr));
+            strtok_r(NULL, " ", &saveptr);
+            strtok_r(NULL, " ", &saveptr);
+            strtok_r(NULL, " ", &saveptr);
+            strncpy(wg->name, strtok_r(NULL, " ", &saveptr), sizeof(wg->name));
+
+            /* We got one more widget. */
+            wg++;
+            widgets++;
+        }
+        else if((strstr(line, "hsl") != NULL) &&
+                (strstr(line, "pod_") != NULL))
+        {
+            wg->id = PD_HSLIDER;
+
+            strtok_r(line, " ", &saveptr);
+            strtok_r(NULL, " ", &saveptr);
+            wg->x = atoi(strtok_r(NULL, " ", &saveptr)) * screen_multiplier;
+            wg->y = atoi(strtok_r(NULL, " ", &saveptr)) * screen_multiplier;
+            strtok_r(NULL, " ", &saveptr);
+            wg->w = atoi(strtok_r(NULL, " ", &saveptr)) * screen_multiplier;
+            wg->h = atoi(strtok_r(NULL, " ", &saveptr)) * screen_multiplier;
+            wg->min = atoi(strtok_r(NULL, " ", &saveptr));
+            wg->max = atoi(strtok_r(NULL, " ", &saveptr));
+            strtok_r(NULL, " ", &saveptr);
+            strtok_r(NULL, " ", &saveptr);
+            strtok_r(NULL, " ", &saveptr);
+            strncpy(wg->name, strtok_r(NULL, " ", &saveptr), sizeof(wg->name));
+
+            /* We got one more widget. */
+            wg++;
+            widgets++;
+        }
+        else if((strstr(line, "vradio") != NULL) &&
+                (strstr(line, "pod_") != NULL))
+        {
+            wg->id = PD_VRADIO;
+
+            strtok_r(line, " ", &saveptr);
+            strtok_r(NULL, " ", &saveptr);
+            wg->x = atoi(strtok_r(NULL, " ", &saveptr)) * screen_multiplier;
+            wg->y = atoi(strtok_r(NULL, " ", &saveptr)) * screen_multiplier;
+            strtok_r(NULL, " ", &saveptr);
+            wg->w = atoi(strtok_r(NULL, " ", &saveptr)) * screen_multiplier;
+            strtok_r(NULL, " ", &saveptr);
+            strtok_r(NULL, " ", &saveptr);
+            wg->min = 0;
+            wg->max = atoi(strtok_r(NULL, " ", &saveptr));
+            wg->h = wg->w * wg->max;
+            strtok_r(NULL, " ", &saveptr);
+            strncpy(wg->name, strtok_r(NULL, " ", &saveptr), sizeof(wg->name));
+            wg->max--;
+
+            /* We got one more widget. */
+            wg++;
+            widgets++;
+        }
+        else if((strstr(line, "hradio") != NULL) &&
+                (strstr(line, "pod_") != NULL))
+        {
+            wg->id = PD_HRADIO;
+
+            strtok_r(line, " ", &saveptr);
+            strtok_r(NULL, " ", &saveptr);
+            wg->x = atoi(strtok_r(NULL, " ", &saveptr)) * screen_multiplier;
+            wg->y = atoi(strtok_r(NULL, " ", &saveptr)) * screen_multiplier;
+            strtok_r(NULL, " ", &saveptr);
+            wg->h = atoi(strtok_r(NULL, " ", &saveptr)) * screen_multiplier;
+            strtok_r(NULL, " ", &saveptr);
+            strtok_r(NULL, " ", &saveptr);
+            wg->min = 0;
+            wg->max = atoi(strtok_r(NULL, " ", &saveptr));
+            wg->w = wg->h * wg->max;
+            strtok_r(NULL, " ", &saveptr);
+            strncpy(wg->name, strtok_r(NULL, " ", &saveptr), sizeof(wg->name));
+            wg->max--;
+
+            /* We got one more widget. */
+            wg++;
+            widgets++;
+        }
+        else if((strstr(line, "bng") != NULL) &&
+                (strstr(line, "pod_") != NULL))
+        {
+            wg->id = PD_BANG;
+
+            strtok_r(line, " ", &saveptr);
+            strtok_r(NULL, " ", &saveptr);
+            wg->x = atoi(strtok_r(NULL, " ", &saveptr)) * screen_multiplier;
+            wg->y = atoi(strtok_r(NULL, " ", &saveptr)) * screen_multiplier;
+            strtok_r(NULL, " ", &saveptr);
+            wg->w = atoi(strtok_r(NULL, " ", &saveptr)) * screen_multiplier;
+            wg->h = wg->w;
+            strtok_r(NULL, " ", &saveptr);
+            strtok_r(NULL, " ", &saveptr);
+            strtok_r(NULL, " ", &saveptr);
+            strtok_r(NULL, " ", &saveptr);
+            strncpy(wg->name, strtok_r(NULL, " ", &saveptr), sizeof(wg->name));
+            wg->min = 0;
+            wg->max = 1;
+
+            /* Clear timeout flag. */
+            wg->timeout = 0;
+
+            /* We got one more widget. */
+            wg++;
+            widgets++;
+        }
+        else if(strstr(line, "text") != NULL)
+        {
+            wg->id = PD_TEXT;
+
+            strtok_r(line, " ", &saveptr);
+            strtok_r(NULL, " ", &saveptr);
+            wg->x = atoi(strtok_r(NULL, " ", &saveptr)) * screen_multiplier;
+            wg->y = atoi(strtok_r(NULL, " ", &saveptr)) * screen_multiplier;
+            strncpy(wg->name, strtok_r(NULL, " ", &saveptr), sizeof(wg->name));
+            char* w = strtok_r(NULL, " ", &saveptr);
+            while(w != NULL)
+            {
+                strcat(wg->name, w);
+                strcat(wg->name, " ");
+                w = strtok_r(NULL, " ", &saveptr);
+            }
+            /* Cut off unneeded characters (';' and '\n'). */
+            int namelen = strlen(wg->name);
+            if(namelen > 1)
+            {
+                /* Cut off '\n'. */
+                wg->name[namelen-1] = '\0';
+                namelen--;
+                /* Cut the last semi-colon, if there is one. */
+                if(wg->name[namelen-1] == ';')
+                    wg->name[namelen-1] = '\0';
+            }
+
+
+            /* We got one more widget. */
+            wg++;
+            widgets++;
+        }
+    }
+
+    /* Close PD patch. */
+    close(fd);
+
+    /* Return amount of loaded widgets. */
+    return widgets;
+}
+
+/* Draw standard user interface. */
+void pd_gui_draw_standard(void)
+{
+    /* Draw main circle. */
+    rb->lcd_set_foreground(FGCOLOR);
+    fillcircle(LCD_WIDTH / 2,
+               LCD_HEIGHT / 2,
+               2 * MIN(LCD_WIDTH, LCD_HEIGHT) / 5);
+
+    /* Draw center circle. */
+    rb->lcd_set_foreground(action_on ? BTNCOLOR_DARK : BTNCOLOR_LIGHT);
+    fillcircle(LCD_WIDTH / 2,
+               LCD_HEIGHT / 2,
+               MIN(LCD_WIDTH, LCD_HEIGHT) / 8);
+
+    /* Draw pressed buttons. */
+    if(play_on)
+        fillcircle(LCD_WIDTH / 2,
+                   3 * LCD_HEIGHT / 4,
+                   MIN(LCD_WIDTH, LCD_HEIGHT) / 8);
+
+    if(previous_on)
+        fillcircle(LCD_WIDTH / 3,
+                   LCD_HEIGHT / 2,
+                   MIN(LCD_WIDTH, LCD_HEIGHT) / 8);
+
+    if(next_on)
+        fillcircle(2 * LCD_WIDTH / 3 + 1,
+                   LCD_HEIGHT / 2,
+                   MIN(LCD_WIDTH, LCD_HEIGHT) / 8);
+
+    if(menu_on)
+        fillcircle(LCD_WIDTH / 2,
+                   LCD_HEIGHT / 4,
+                   MIN(LCD_WIDTH, LCD_HEIGHT) / 8);
+
+    /* Restore foreground color. */
+    rb->lcd_set_foreground(FGCOLOR);
+}
+
+/* Draw custom user interface. */
+void pd_gui_draw_custom(struct pd_widget* wg, unsigned int widgets)
+{
+    unsigned int i;
+    int j;
+    int v;
+
+    for(i = 0; i < widgets; wg++, i++)
+    {
+        switch(wg->id)
+        {
+            case PD_BANG:
+                /* Clear area to (re-)draw. */
+                rb->lcd_set_foreground(BGCOLOR);
+                rb->lcd_fillrect(wg->x, wg->y, wg->w, wg->h);
+                rb->lcd_set_foreground(FGCOLOR);
+                /* Draw border (rectangle). */
+                rb->lcd_drawrect(wg->x, wg->y, wg->w, wg->h);
+                /* Draw button (circle), being filled depending on value. */
+                if(((int) wg->value) == 0) /* Button not pressed. */
+                    drawcircle(wg->x + wg->w/2,
+                               wg->y + wg->w/2,
+                               wg->w/2 - 1);
+                else /* Button pressed. */
+                    fillcircle(wg->x + wg->w/2,
+                               wg->y + wg->w/2,
+                               wg->w/2 - 1);
+                break;
+
+            case PD_VSLIDER:
+                /* Clear area to (re-)draw. */
+                rb->lcd_set_foreground(BGCOLOR);
+                rb->lcd_fillrect(wg->x, wg->y, wg->w, wg->h);
+                rb->lcd_set_foreground(FGCOLOR);
+                /* Draw border. */
+                rb->lcd_drawrect(wg->x, wg->y, wg->w, wg->h);
+                /* Draw slider. */
+                v = ((float) wg->h / (wg->max - wg->min)) *
+                    (wg->max - wg->value);
+                rb->lcd_fillrect(wg->x, wg->y + v, wg->w, 2);
+                break;
+
+            case PD_HSLIDER:
+                /* Clear area to (re-)draw. */
+                rb->lcd_set_foreground(BGCOLOR);
+                rb->lcd_fillrect(wg->x, wg->y, wg->w, wg->h);
+                rb->lcd_set_foreground(FGCOLOR);
+                /* Draw border. */
+                rb->lcd_drawrect(wg->x, wg->y, wg->w, wg->h);
+                /* Draw slider. */
+                v = ((float) wg->w / (wg->max - wg->min)) *
+                    (wg->max - wg->value);
+                rb->lcd_fillrect(wg->x + wg->w - v, wg->y, 2, wg->h);
+                break;
+
+            case PD_HRADIO:
+                /* Clear area to (re-)draw. */
+                rb->lcd_set_foreground(BGCOLOR);
+                rb->lcd_fillrect(wg->x, wg->y, wg->w, wg->h);
+                rb->lcd_set_foreground(FGCOLOR);
+                for(j = 0; j < wg->w / wg->h; j++)
+                {
+                    /* Draw border. */
+                    rb->lcd_drawrect(wg->x + wg->h * j, wg->y, wg->h, wg->h);
+                    /* If marked, draw button. */
+                    if(((int) wg->value) == j)
+                        rb->lcd_fillrect(wg->x + wg->h * j + 2, wg->y + 2,
+                                         wg->h - 4, wg->h - 4);
+                }
+                break;
+
+            case PD_VRADIO:
+                /* Clear area to (re-)draw. */
+                rb->lcd_set_foreground(BGCOLOR);
+                rb->lcd_fillrect(wg->x, wg->y, wg->w, wg->h);
+                rb->lcd_set_foreground(FGCOLOR);
+                for(j = 0; j < wg->h / wg->w; j++)
+                {
+                    /* Draw border. */
+                    rb->lcd_drawrect(wg->x, wg->y + wg->w * j, wg->w, wg->w);
+                    /* If marked, draw button. */
+                    if(((int) wg->value) == j)
+                        rb->lcd_fillrect(wg->x + 2, wg->y + wg->w * j + 2,
+                                         wg->w - 4, wg->w - 4);
+                }
+                break;
+
+            case PD_NUMBER:
+                rb->lcd_hline(wg->x,
+                              wg->x + wg->w - number_corner,
+                              wg->y);
+                rb->lcd_drawline(wg->x + wg->w - number_corner,
+                                 wg->y,
+                                 wg->x + wg->w,
+                                 wg->y + number_corner);
+                rb->lcd_vline(wg->x + wg->w,
+                              wg->y + number_corner,
+                              wg->y + wg->h);
+                rb->lcd_hline(wg->x,
+                              wg->x + wg->w,
+                              wg->y + wg->h);
+                rb->lcd_vline(wg->x,
+                              wg->y,
+                              wg->y + wg->h);
+                char sbuf[12];
+                ftoan(wg->value, sbuf, 12);
+                rb->lcd_putsxy(wg->x + 2, wg->y + 2, sbuf);
+                break;
+
+            case PD_TEXT:
+                rb->lcd_putsxy(wg->x + 2, wg->y, wg->name);
+                break;
+
+            case PD_SYMBOL:
+                break;
+        }
+    }
+}
+
+/* Draw the GUI. */
+void pd_gui_draw(struct pd_widget* wg, unsigned int widgets)
+{
+    /* Draw GUI. */
+    if(widgets == 0)
+        pd_gui_draw_standard();
+    else
+        pd_gui_draw_custom(wg, widgets);
+
+    /* Update display. */
+    rb->lcd_update();
+}
+
+/* Parse buttons, if needed, send messages to the Pure Data code. */
+bool pd_gui_parse_buttons(unsigned int widgets)
+{
+    static long last_bv = 0;
+    long bv = rb->button_get(false);
+
+    /* Extract differences between current and previous button values. */
+    long diff_bv = bv ^ last_bv;
+
+    /* If no difference since the last button value, return here. */
+    if(diff_bv == 0)
+        return false;
+
+    /* Query whether we have to quit. */
+    if(bv == PDPOD_QUIT)
+    {
+        /* No need to send the quitting message to Pure Data core,
+           as setting the quit flag ends threads and jumps
+           to the cleanup code. */
+        quit = true;
+        return false;
+    }
+
+    /* Check the action (shift, mode) button. */
+    if(diff_bv & PDPOD_ACTION)
+    {
+        if(bv & PDPOD_ACTION)
+        {
+            SEND_TO_CORE("b;\n");
+
+            if(widgets == 0)
+                action_on = true;
+        }
+        else
+        {
+            if(widgets == 0)
+                action_on = false;
+        }
+    }
+
+    /* Check play button. */
+    if(diff_bv & PDPOD_PLAY)
+    {
+        if(bv & PDPOD_PLAY)
+        {
+            /* Action + play = pause. */
+            if(action_on)
+            {
+                /* Switch paused state. */
+                paused = !paused;
+                SEND_TO_CORE(paused ? "p 1;\n" : "p 0;\n");
+            }
+
+            if(!action_on && !paused)
+                SEND_TO_CORE("d 1;\n");
+
+            if(widgets == 0)
+                play_on = true;
+        }
+        else
+        {
+            if(!action_on && !paused)
+                SEND_TO_CORE("d 0;\n");
+
+            if(widgets == 0)
+                play_on = false;
+        }
+    }
+
+    /* Check rewind (previous) button. */
+    if(diff_bv & PDPOD_PREVIOUS)
+    {
+        if(bv & PDPOD_PREVIOUS)
+        {
+            if(!paused)
+                SEND_TO_CORE("w 1;\n");
+
+            if(widgets == 0)
+                previous_on = true;
+        }
+        else
+        {
+            if(!paused)
+                SEND_TO_CORE("w 0;\n");
+
+            if(widgets == 0)
+                previous_on = false;
+        }
+    }
+
+    /* Check forward (next) button. */
+    if(diff_bv & PDPOD_NEXT)
+    {
+        if(bv & PDPOD_NEXT)
+        {
+            if(!paused)
+                SEND_TO_CORE("f 1;\n");
+
+            if(widgets == 0)
+                next_on = true;
+        }
+        else
+        {
+            if(!paused)
+                SEND_TO_CORE("f 0;\n");
+
+            if(widgets == 0)
+                next_on = false;
+        }
+    }
+
+    /* Check menu (select) button. */
+    if(diff_bv & PDPOD_MENU)
+    {
+        if(bv & PDPOD_MENU)
+        {
+            if(!action_on && !paused)
+                SEND_TO_CORE("m 1;\n");
+
+            if(widgets == 0)
+                menu_on = true;
+        }
+        else
+        {
+            if(!action_on && !paused)
+                SEND_TO_CORE("m 0;\n");
+
+            if(widgets == 0)
+                menu_on = false;
+        }
+    }
+
+    /* Check scroll right (or up) button. */
+    if(diff_bv & PDPOD_WHEELRIGHT)
+    {
+        if(bv & PDPOD_WHEELRIGHT)
+        {
+            SEND_TO_CORE(action_on ? "r 10;\n" : "r 1;\n");
+        }
+    }
+
+    /* Check scroll left (or down) button. */
+    if(diff_bv & PDPOD_WHEELLEFT)
+    {
+        if(bv & PDPOD_WHEELLEFT)
+        {
+            SEND_TO_CORE(action_on ? "l 10;\n" : "l 1;\n");
+        }
+    }
+
+    /* Backup button value. */
+    last_bv = bv;
+
+    /* GUI has to be updated. */
+    return true;
+}
+
+/* Emulate timer for widgets which use time-out. */
+bool pd_gui_apply_timeouts(struct pd_widget* wg, unsigned int widgets)
+{
+    unsigned int i;
+    bool result = false;
+
+    for(i = 0; i < widgets; wg++, i++)
+    {
+        if(wg->id == PD_BANG)
+        {
+            if(wg->timeout > 0)
+            {
+                /* Decrement timeout value. */
+                wg->timeout--;
+
+                /* If zero reached, clear value. */
+                if(wg->timeout == 0)
+                {
+                    wg->value = 0;
+                    result = true;
+                }
+            }
+        }
+    }
+
+    return result;
+}
+
+/* Parse and apply message from the Pure Data core. */
+void pd_gui_parse_message(struct datagram* dg,
+                          struct pd_widget* wg,
+                          unsigned int widgets)
+{
+    unsigned int i;
+    char* saveptr;
+    char* object = NULL;
+    char* argument = NULL;
+    float argvalue = 0;
+
+    object = strtok_r(dg->data, " ", &saveptr);
+    argument = strtok_r(NULL, " ;\n", &saveptr);
+
+    if(argument != NULL)
+        argvalue = atof(argument);
+
+    for(i = 0; i < widgets; wg++, i++)
+    {
+        if(strncmp(object, wg->name, strlen(wg->name)) == 0)
+        {
+            /* If object not a number, set boundaries. */
+            if(wg->id != PD_NUMBER)
+            {
+                if(argvalue > wg->max)
+                    argvalue = wg->max;
+                else if(argvalue < wg->min)
+                    argvalue = wg->min;
+            }
+
+            /* Set value. */
+            if(wg->id == PD_BANG)
+            {
+                wg->value = 1;
+                wg->timeout = HZ / 10;
+            }
+            else
+            {
+                wg->value = argvalue;
+            }
+        }
+    }
+}
diff --git a/apps/plugins/pdbox/pdbox.c b/apps/plugins/pdbox/pdbox.c
index dd848de..5c5677c 100644
--- a/apps/plugins/pdbox/pdbox.c
+++ b/apps/plugins/pdbox/pdbox.c
@@ -44,6 +44,7 @@
 int sys_chinlist[MAXAUDIOINDEV];
 int sys_soundoutdevlist[MAXAUDIOOUTDEV];
 int sys_choutlist[MAXAUDIOOUTDEV];
+t_binbuf* inbinbuf;
 
 /* References for scheduler variables and functions. */
 extern t_time sys_time;
@@ -59,7 +60,7 @@
 bool quit = false;
 
 /* Stack sizes for threads. */
-#define CORESTACKSIZE (8 * 1024 * 1024)
+#define CORESTACKSIZE (1 * 1024 * 1024)
 #define GUISTACKSIZE (512 * 1024)
 
 /* Thread stacks. */
@@ -74,30 +75,45 @@
 /* GUI thread */
 void gui_thread(void)
 {
-/*    struct datagram pong; */
+    struct pd_widget widget[128];
+    unsigned int widgets = 0;
+    struct datagram dg;
+    bool update;
+
+    /* Initialize GUI. */
+    pd_gui_init();
+
+    /* Load PD patch. */
+    widgets = pd_gui_load_patch(widget,
+                                sizeof(widget) / sizeof(struct pd_widget));
+
+    /* Draw initial state of UI. */
+    pd_gui_draw(widget, widgets);
 
     /* GUI loop */
     while(!quit)
     {
-#if 0
-        /* Send ping to the core. */
-        SEND_TO_CORE("Ping!");
-        rb->splash(HZ, "Sent ping.");
+        /* Reset update flag. */
+        update = false;
 
-        /* Wait for answer. */
-        while(!RECEIVE_FROM_CORE(&pong))
-            rb->yield();
+        /* Apply timer to widgets. */
+        update |=
+        pd_gui_apply_timeouts(widget, widgets);
 
-        /* If got a pong -- everything allright. */
-        if(memcmp("Pong!", pong.data, pong.size) == 0)
+        /* Process buttons. */
+        update |=
+        pd_gui_parse_buttons(widgets);
+
+        /* Receive and parse datagrams. */
+        while(RECEIVE_FROM_CORE(&dg))
         {
-            rb->splash(HZ, "Got pong!");
-            quit = true;
-            break;
+            update = true;
+            pd_gui_parse_message(&dg, widget, widgets);
         }
-#endif
-        if(rb->button_get(false) == BUTTON_OFF)
-            quit = true;
+
+        /* If there is something to update in GUI, do so. */
+        if(update)
+            pd_gui_draw(widget, widgets);
 
         rb->sleep(1);
     }
@@ -120,6 +136,12 @@
     /* Core scheduler loop */
     while(!quit)
     {
+        /* Receive datagrams. */
+        struct datagram dg;
+
+        while(RECEIVE_TO_CORE(&dg))
+            rockbox_receive_callback(&dg);
+
         /* Use sys_send_dacs() function as timer. */
         while(sys_send_dacs() != SENDDACS_NO)
             sched_tick(sys_time + sys_time_per_dsp_tick);
@@ -194,6 +216,9 @@
         return PLUGIN_ERROR;
     }
 
+    /* Boost CPU. */
+    cpu_boost(true);
+
     /* Start threads. */
     core_thread_id =
         rb->create_thread(&core_thread,
@@ -237,6 +262,9 @@
     rb->thread_wait(gui_thread_id);
     rb->thread_wait(core_thread_id);
 
+    /* Unboost CPU. */
+    cpu_boost(false);
+
     /* Close audio subsystem. */
     sys_close_audio();
 
diff --git a/apps/plugins/pdbox/pdbox.h b/apps/plugins/pdbox/pdbox.h
index 47aa2ec..5f30013 100644
--- a/apps/plugins/pdbox/pdbox.h
+++ b/apps/plugins/pdbox/pdbox.h
@@ -197,4 +197,74 @@
 #define sinh rb_sinh
 #define tan rb_tan
 
+#define strtok_r rb->strtok_r
+#define strstr rb->strcasestr
+
+
+/* PdPod GUI declarations. */
+
+enum pd_widget_id
+{
+    PD_BANG,
+    PD_VSLIDER,
+    PD_HSLIDER,
+    PD_VRADIO,
+    PD_HRADIO,
+    PD_NUMBER,
+    PD_SYMBOL,
+    PD_TEXT
+};
+
+struct pd_widget
+{
+    enum pd_widget_id id;
+    char name[128];
+    int x;
+    int y;
+    int w;
+    int h;
+    int min;
+    int max;
+    float value;
+    int timeout;
+};
+
+enum pd_key_id
+{
+    KEY_PLAY,
+    KEY_REWIND,
+    KEY_FORWARD,
+    KEY_MENU,
+    KEY_ACTION,
+    KEY_WHEELLEFT,
+    KEY_WHEELRIGHT,
+    PD_KEYS
+};
+
+/* Map real keys to virtual ones.
+   Feel free to add your preferred keymap here. */
+#if defined(IRIVER_H300_SERIES)
+    /* Added by wincent */
+    #define PDPOD_QUIT (BUTTON_OFF)
+    #define PDPOD_PLAY (BUTTON_ON)
+    #define PDPOD_PREVIOUS (BUTTON_LEFT)
+    #define PDPOD_NEXT (BUTTON_RIGHT)
+    #define PDPOD_MENU (BUTTON_SELECT)
+    #define PDPOD_WHEELLEFT (BUTTON_DOWN)
+    #define PDPOD_WHEELRIGHT (BUTTON_UP)
+    #define PDPOD_ACTION (BUTTON_MODE)
+/* #elif defined(IRIVER_H100_SERIES) */
+#else
+    #warning "No keys defined for this architecture!"
+#endif
+
+/* Prototype of GUI functions. */
+void pd_gui_init(void);
+unsigned int pd_gui_load_patch(struct pd_widget* wg, unsigned int max_widgets);
+void pd_gui_draw(struct pd_widget* wg, unsigned int widgets);
+bool pd_gui_parse_buttons(unsigned int widgets);
+void pd_gui_parse_message(struct datagram* dg,
+                          struct pd_widget* wg, unsigned int widgets);
+bool pd_gui_apply_timeouts(struct pd_widget* wg, unsigned int widgets);
+
 #endif