changeset 201:63182715cf02 main

checkpoint
author Bob Cook <bob@bobcookdev.com>
date Sat, 11 Jan 2014 18:00:27 -0800
parents 439e242e672a
children e283b09c8a17
files main/robots/odr-display-4d/bcui.inc main/robots/odr-display-4d/odr-display-4d.4dg main/robots/odr-display-4d/odr-display-4d.cfg
diffstat 3 files changed, 668 insertions(+), 552 deletions(-) [+]
line wrap: on
line diff
--- a/main/robots/odr-display-4d/bcui.inc	Sun Jan 05 16:52:45 2014 -0800
+++ b/main/robots/odr-display-4d/bcui.inc	Sat Jan 11 18:00:27 2014 -0800
@@ -1,382 +1,453 @@
-// ----------------------------------------------------------------------------------------
-//
-//  Copyright (C) 2014 Bob Cook
-//
-//  Bob Cook Development, Robotics Library
-//  http://www.bobcookdev.com/rl/
-//
-//  bcui is a simple UI widget framework for the 4D Systems Picaso LCD display products.
-//
-//  Permission is hereby granted, free of charge, to any person obtaining a copy
-//  of this software and associated documentation files (the "Software"), to deal
-//  in the Software without restriction, including without limitation the rights
-//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-//  copies of the Software, and to permit persons to whom the Software is
-//  furnished to do so, subject to the following conditions:
-//
-//  The above copyright notice and this permission notice shall be included in
-//  all copies or substantial portions of the Software.
-//
-//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-//  THE SOFTWARE.
-//
-// ----------------------------------------------------------------------------------------
-
-#constant BCUI_WIDGET_TYPE_BUTTON := 0
-#constant BCUI_WIDGET_TYPE_ARROW  := 1
-#constant BCUI_WIDGET_TYPE_TEXT   := 2
-
-#constant BCUI_WIDGET_ARROW_LEFT  := 0
-#constant BCUI_WIDGET_ARROW_UP    := 1
-#constant BCUI_WIDGET_ARROW_RIGHT := 2
-#constant BCUI_WIDGET_ARROW_DOWN  := 3
-
-// ----------------------------------------------------------------------------------------
-
-#constant BCUI_VIEW_INFO_SIZE := ( 1 + 1 + 1 ) // for the fields below
-#constant BCUI_VIEW_INFO_OFFSET_LINK    := 0
-#constant BCUI_VIEW_INFO_OFFSET_WIDGETS := 1
-#constant BCUI_VIEW_INFO_OFFSET_COLOR   := 2
-
-#constant BCUI_WIDGET_INFO_SIZE := ( 1 + 1 + 1 + 2 + 4 + 1 + 1 ) // for the fields below
-#constant BCUI_WIDGET_INFO_OFFSET_LINK  := 0
-#constant BCUI_WIDGET_INFO_OFFSET_OWNER := 1
-#constant BCUI_WIDGET_INFO_OFFSET_TYPE  := 2
-#constant BCUI_WIDGET_INFO_OFFSET_X     := 3
-#constant BCUI_WIDGET_INFO_OFFSET_Y     := 4
-#constant BCUI_WIDGET_INFO_OFFSET_RECT  := 5 // x1, y1, x2, y2
-#constant BCUI_WIDGET_INFO_OFFSET_COLOR := 9
-#constant BCUI_WIDGET_INFO_OFFSET_LABEL := 10
-
-#constant BCUI_WIDGET_BUTTON_HEIGHT := 26
-#constant BCUI_WIDGET_BUTTON_WIDTH  := 70
-#constant BCUI_WIDGET_BUTTON_CORNER :=  8
-
-#constant BCUI_WIDGET_ARROW_DIAMETER := 32
-#constant BCUI_WIDGET_ARROW_OFFSET_1 :=  8
-#constant BCUI_WIDGET_ARROW_OFFSET_2 := 10
-
-#constant BCUI_WIDGET_TEXT_HEIGHT := 26
-
-// ----------------------------------------------------------------------------------------
-
-var bcui_view_list; // linked list of view info structures
-var bcui_current_view; // active_view or 0 if no active view
-
-// ----------------------------------------------------------------------------------------
-
-func bcui_init()
-    bcui_view_list    := 0;
-    bcui_current_view := 0;
-endfunc
-
-// ----------------------------------------------------------------------------------------
-
-func bcui_pt_in_widget( var x, var y, var widget )
-    if ( x >= widget[ BCUI_WIDGET_INFO_OFFSET_RECT + 0 ]
-      && x <= widget[ BCUI_WIDGET_INFO_OFFSET_RECT + 2 ]
-      && y >= widget[ BCUI_WIDGET_INFO_OFFSET_RECT + 1 ]
-      && y <= widget[ BCUI_WIDGET_INFO_OFFSET_RECT + 3 ] )
-        return ( TRUE );
-    else
-        return ( FALSE );
-    endif
-endfunc
-
-// ----------------------------------------------------------------------------------------
-
-func bcui_darken( var color )
-    return ( ( ( color >> 12 ) & 0x0f ) << 11 )
-           | ( ( ( color >> 6 ) & 0x1f ) << 5 )
-       | ( ( color >> 1 ) & 0x0f );
-endfunc
-
-// ----------------------------------------------------------------------------------------
-
-func bcui_create_view( var background )
-
-    var private view;
-
-    view := mem_AllocZ( BCUI_VIEW_INFO_SIZE * 2 ); // 2x for bytes from words
-    view[ BCUI_VIEW_INFO_OFFSET_LINK ] := bcui_view_list;
-    bcui_view_list := view;
-
-    view[ BCUI_VIEW_INFO_OFFSET_WIDGETS ] := 0;
-    view[ BCUI_VIEW_INFO_OFFSET_COLOR ]   := background;
-
-endfunc
-
-// ----------------------------------------------------------------------------------------
-
-func bcui_create_button( var view, var center_x, var center_y, var label, var color )
-
-    var private button;
-
-    button := mem_AllocZ( BCUI_WIDGET_INFO_SIZE * 2 ); // 2x for bytes from words
-    button[ BCUI_WIDGET_INFO_OFFSET_LINK ] := view[ BCUI_VIEW_INFO_OFFSET_WIDGETS ];
-    view[ BCUI_VIEW_INFO_OFFSET_WIDGETS ]  := button;
-
-    button[ BCUI_WIDGET_INFO_OFFSET_OWNER ] := view;
-    button[ BCUI_WIDGET_INFO_OFFSET_TYPE ]  := BCUI_WIDGET_TYPE_BUTTON;
-
-    button[ BCUI_WIDGET_INFO_OFFSET_RECT + 0 ] := center_x - ( BCUI_WIDGET_BUTTON_WIDTH  / 2 );
-    button[ BCUI_WIDGET_INFO_OFFSET_RECT + 1 ] := center_y - ( BCUI_WIDGET_BUTTON_HEIGHT / 2 );
-    button[ BCUI_WIDGET_INFO_OFFSET_RECT + 2 ] := center_x + ( BCUI_WIDGET_BUTTON_WIDTH  / 2 );
-    button[ BCUI_WIDGET_INFO_OFFSET_RECT + 3 ] := center_y + ( BCUI_WIDGET_BUTTON_HEIGHT / 2 );
-
-    button[ BCUI_WIDGET_INFO_OFFSET_X ] := center_x;
-    button[ BCUI_WIDGET_INFO_OFFSET_Y ] := center_y;
-
-    button[ BCUI_WIDGET_INFO_OFFSET_COLOR ] := color;
-
-    button[ BCUI_WIDGET_INFO_OFFSET_LABEL ] := mem_AllocZ( str_Length( label ) + 1 );
-    str_Copy( str_Ptr( button[ BCUI_WIDGET_INFO_OFFSET_LABEL ] ), label );
-
-    return button;
-
-endfunc
-
-// ----------------------------------------------------------------------------------------
-
-func bcui_create_arrow( var view, var center_x, var center_y, var direction, var color )
-
-    var private arrow;
-
-    arrow := mem_AllocZ( BCUI_WIDGET_INFO_SIZE * 2 ); // 2x for bytes from words
-    arrow[ BCUI_WIDGET_INFO_OFFSET_LINK ] := view[ BCUI_VIEW_INFO_OFFSET_WIDGETS ];
-    view[ BCUI_VIEW_INFO_OFFSET_WIDGETS ] := arrow;
-
-    arrow[ BCUI_WIDGET_INFO_OFFSET_OWNER ] := view;
-    arrow[ BCUI_WIDGET_INFO_OFFSET_TYPE ]  := BCUI_WIDGET_TYPE_ARROW;
-
-    arrow[ BCUI_WIDGET_INFO_OFFSET_RECT + 0 ] := center_x - ( BCUI_WIDGET_ARROW_DIAMETER / 2 );
-    arrow[ BCUI_WIDGET_INFO_OFFSET_RECT + 1 ] := center_y - ( BCUI_WIDGET_ARROW_DIAMETER / 2 );
-    arrow[ BCUI_WIDGET_INFO_OFFSET_RECT + 2 ] := center_x + ( BCUI_WIDGET_ARROW_DIAMETER / 2 );
-    arrow[ BCUI_WIDGET_INFO_OFFSET_RECT + 3 ] := center_y + ( BCUI_WIDGET_ARROW_DIAMETER / 2 );
-
-    arrow[ BCUI_WIDGET_INFO_OFFSET_X ] := center_x;
-    arrow[ BCUI_WIDGET_INFO_OFFSET_Y ] := center_y;
-
-    arrow[ BCUI_WIDGET_INFO_OFFSET_LABEL ] := direction; // left, up, right, down
-
-    arrow[ BCUI_WIDGET_INFO_OFFSET_COLOR ] := color;
-
-    return arrow;
-
-endfunc
-
-// ----------------------------------------------------------------------------------------
-
-func bcui_do_touch() // returns object or 0 if no hit
-
-    if ( bcui_current_view == 0 )
-        return 0;
-    endif
-
-    var private widget;
-
-    widget := bcui_current_view[ BCUI_VIEW_INFO_OFFSET_WIDGETS ];
-
-    while ( widget != 0 )
-
-        var private touch_x, touch_y;
-
-        touch_x := touch_Get( TOUCH_GETX );
-        touch_y := touch_Get( TOUCH_GETY );
-
-        if ( bcui_pt_in_widget( touch_x, touch_y, widget ) )
-
-            bcui_paint_widget( widget, TRUE /* touched */ );
-
-            while ( touch_Get( TOUCH_STATUS ) != TOUCH_RELEASED )
-                pause( 10 );
-            wend
-
-            bcui_paint_widget( widget, FALSE /* not touched */ );
-            return widget;
-
-        endif
-
-        widget := widget[ BCUI_WIDGET_INFO_OFFSET_LINK ];
-
-    wend
-
-    // no match
-    return 0;
-
-endfunc
-
-// ----------------------------------------------------------------------------------------
-
-func bcui_activate_view( var view )
-
-    bcui_current_view := view;
-
-    if ( bcui_current_view == 0 )
-        gfx_RectangleFilled( 0, 0, gfx_Get( X_MAX ), gfx_Get( Y_MAX ), BLACK );
-        return;
-    endif
-
-    gfx_RectangleFilled( 0, 0,
-                         gfx_Get( X_MAX ), gfx_Get( Y_MAX ),
-                         view[ BCUI_VIEW_INFO_OFFSET_COLOR ] );
-
-    var private widget;
-
-    widget := view[ BCUI_VIEW_INFO_OFFSET_WIDGETS ];
-
-    while ( widget != 0 )
-
-        bcui_paint_widget( widget, FALSE /* not touched */ );
-
-        widget := widget[ BCUI_WIDGET_INFO_OFFSET_LINK ];
-
-    wend
-
-endfunc
-
-// ----------------------------------------------------------------------------------------
-
-func bcui_paint_widget( var widget, var as_touched )
-
-    var private widget_color;
-
-    if ( as_touched )
-        widget_color := bcui_darken( widget[ BCUI_WIDGET_INFO_OFFSET_COLOR ] );
-    else
-        widget_color := widget[ BCUI_WIDGET_INFO_OFFSET_COLOR ];
-    endif
-
-    switch ( widget[ BCUI_WIDGET_INFO_OFFSET_TYPE ] )
-
-        case BCUI_WIDGET_TYPE_BUTTON:
-
-            bcui_paint_button( widget[ BCUI_WIDGET_INFO_OFFSET_X ],
-                               widget[ BCUI_WIDGET_INFO_OFFSET_Y ],
-                               widget_color,
-                               widget[ BCUI_WIDGET_INFO_OFFSET_LABEL ] );
-            break;
-
-        case BCUI_WIDGET_TYPE_ARROW:
-
-            bcui_paint_arrow( widget[ BCUI_WIDGET_INFO_OFFSET_X ],
-                              widget[ BCUI_WIDGET_INFO_OFFSET_Y ],
-                              widget_color,
-                              widget[ BCUI_WIDGET_INFO_OFFSET_LABEL ] );
-            break;
-
-    endswitch
-
-endfunc
-
-// ----------------------------------------------------------------------------------------
-
-func bcui_paint_button( var center_x, var center_y, var color, var label )
-
-    // paint the four corners
-    gfx_CircleFilled( center_x - ( ( BCUI_WIDGET_BUTTON_WIDTH  / 2 ) - BCUI_WIDGET_BUTTON_CORNER ),
-                      center_y - ( ( BCUI_WIDGET_BUTTON_HEIGHT / 2 ) - BCUI_WIDGET_BUTTON_CORNER ),
-                      BCUI_WIDGET_BUTTON_CORNER, color );
-    gfx_CircleFilled( center_x - ( ( BCUI_WIDGET_BUTTON_WIDTH  / 2 ) - BCUI_WIDGET_BUTTON_CORNER ),
-                      center_y + ( ( BCUI_WIDGET_BUTTON_HEIGHT / 2 ) - BCUI_WIDGET_BUTTON_CORNER ),
-                      BCUI_WIDGET_BUTTON_CORNER, color );
-    gfx_CircleFilled( center_x + ( ( BCUI_WIDGET_BUTTON_WIDTH  / 2 ) - BCUI_WIDGET_BUTTON_CORNER ),
-                      center_y - ( ( BCUI_WIDGET_BUTTON_HEIGHT / 2 ) - BCUI_WIDGET_BUTTON_CORNER ),
-                      BCUI_WIDGET_BUTTON_CORNER, color );
-    gfx_CircleFilled( center_x + ( ( BCUI_WIDGET_BUTTON_WIDTH  / 2 ) - BCUI_WIDGET_BUTTON_CORNER ),
-                      center_y + ( ( BCUI_WIDGET_BUTTON_HEIGHT / 2 ) - BCUI_WIDGET_BUTTON_CORNER ),
-                      BCUI_WIDGET_BUTTON_CORNER, color );
-
-    // paint the filled rectanges (center, left, right )
-    gfx_RectangleFilled( center_x - ( ( BCUI_WIDGET_BUTTON_WIDTH / 2 ) - BCUI_WIDGET_BUTTON_CORNER ),
-                         center_y - ( BCUI_WIDGET_BUTTON_HEIGHT / 2 ),
-                         center_x + ( ( BCUI_WIDGET_BUTTON_WIDTH / 2 ) - BCUI_WIDGET_BUTTON_CORNER ),
-                         center_y + ( BCUI_WIDGET_BUTTON_HEIGHT / 2 ),
-                         color );
-    gfx_RectangleFilled( center_x - ( BCUI_WIDGET_BUTTON_WIDTH / 2 ),
-                         center_y - ( ( BCUI_WIDGET_BUTTON_HEIGHT / 2 ) - BCUI_WIDGET_BUTTON_CORNER ),
-                         center_x - ( ( BCUI_WIDGET_BUTTON_WIDTH / 2 ) - BCUI_WIDGET_BUTTON_CORNER ),
-                         center_y + ( ( BCUI_WIDGET_BUTTON_HEIGHT / 2 ) - BCUI_WIDGET_BUTTON_CORNER ),
-                         color );
-    gfx_RectangleFilled( center_x + ( ( BCUI_WIDGET_BUTTON_WIDTH  / 2 ) - BCUI_WIDGET_BUTTON_CORNER ),
-                         center_y - ( ( BCUI_WIDGET_BUTTON_HEIGHT / 2 ) - BCUI_WIDGET_BUTTON_CORNER ),
-                         center_x + ( BCUI_WIDGET_BUTTON_WIDTH / 2 ),
-                         center_y + ( ( BCUI_WIDGET_BUTTON_HEIGHT / 2 ) - BCUI_WIDGET_BUTTON_CORNER ),
-                         color );
-
-    // paint the label
-    txt_Set( FONT_ID, hFont1 );
-    txt_Set( TEXT_COLOUR, WHITE );
-    txt_Set( TEXT_HIGHLIGHT, color );
-    var private w, h;
-    w := strwidth( label );
-    h := strheight();
-    gfx_MoveTo( center_x - ( w / 2 ), center_y - ( h / 2 ) );
-    putstr( label );
-
-endfunc
-
-// ----------------------------------------------------------------------------------------
-
-func bcui_paint_arrow( var center_x, var center_y, var color, var direction )
-
-    // paint the circle itself
-    gfx_CircleFilled( center_x, center_y, ( BCUI_WIDGET_ARROW_DIAMETER / 2 ), color );
-
-    // paint the arrow inside, pointing in the right direction
-    switch ( direction )
-
-        case BCUI_WIDGET_ARROW_LEFT:
-
-            gfx_Triangle( ( center_x - BCUI_WIDGET_ARROW_OFFSET_2 ),
-                          center_y,
-                          ( center_x + ( BCUI_WIDGET_ARROW_OFFSET_2 / 2 ) ),
-                          ( center_y - BCUI_WIDGET_ARROW_OFFSET_1 ),
-                          ( center_x + ( BCUI_WIDGET_ARROW_OFFSET_2 / 2 ) ),
-                          ( center_y + BCUI_WIDGET_ARROW_OFFSET_1 ),
-                          WHITE );
-            break;
-
-        case BCUI_WIDGET_ARROW_UP:
-
-            gfx_Triangle( center_x,
-                          ( center_y - BCUI_WIDGET_ARROW_OFFSET_2 ),
-                          ( center_x - BCUI_WIDGET_ARROW_OFFSET_1 ),
-                          ( center_y + ( BCUI_WIDGET_ARROW_OFFSET_2 / 2 ) ),
-                          ( center_x + BCUI_WIDGET_ARROW_OFFSET_1 ),
-                          ( center_y + ( BCUI_WIDGET_ARROW_OFFSET_2 / 2 ) ),
-                          WHITE );
-            break;
-
-        case BCUI_WIDGET_ARROW_RIGHT:
-
-            gfx_Triangle( ( center_x + BCUI_WIDGET_ARROW_OFFSET_2 ),
-                          center_y,
-                          ( center_x - ( BCUI_WIDGET_ARROW_OFFSET_2 / 2 ) ),
-                          ( center_y - BCUI_WIDGET_ARROW_OFFSET_1 ),
-                          ( center_x - ( BCUI_WIDGET_ARROW_OFFSET_2 / 2 ) ),
-                          ( center_y + BCUI_WIDGET_ARROW_OFFSET_1 ),
-                          WHITE );
-            break;
-
-        case BCUI_WIDGET_ARROW_DOWN:
-
-            gfx_Triangle( center_x,
-                          ( center_y + BCUI_WIDGET_ARROW_OFFSET_2 ),
-                          ( center_x - BCUI_WIDGET_ARROW_OFFSET_1 ),
-                          ( center_y - ( BCUI_WIDGET_ARROW_OFFSET_2 / 2 ) ),
-                          ( center_x + BCUI_WIDGET_ARROW_OFFSET_1 ),
-                          ( center_y - ( BCUI_WIDGET_ARROW_OFFSET_2 / 2 ) ),
-                          WHITE );
-            break;
-
-    endswitch
-
-endfunc
-
-// ----------------------------------------------------------------------------------------
+// ----------------------------------------------------------------------------------------
+//
+//  Copyright (C) 2014 Bob Cook
+//
+//  Bob Cook Development, Robotics Library
+//  http://www.bobcookdev.com/rl/
+//
+//  bcui is a simple UI widget framework for the 4D Systems Picaso LCD display products.
+//
+//  Permission is hereby granted, free of charge, to any person obtaining a copy
+//  of this software and associated documentation files (the "Software"), to deal
+//  in the Software without restriction, including without limitation the rights
+//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+//  copies of the Software, and to permit persons to whom the Software is
+//  furnished to do so, subject to the following conditions:
+//
+//  The above copyright notice and this permission notice shall be included in
+//  all copies or substantial portions of the Software.
+//
+//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+//  THE SOFTWARE.
+//
+// ----------------------------------------------------------------------------------------
+
+#constant BCUI_WIDGET_TYPE_BUTTON := 0
+#constant BCUI_WIDGET_TYPE_ARROW  := 1
+
+#constant BCUI_WIDGET_ARROW_LEFT  := 0
+#constant BCUI_WIDGET_ARROW_UP    := 1
+#constant BCUI_WIDGET_ARROW_RIGHT := 2
+#constant BCUI_WIDGET_ARROW_DOWN  := 3
+
+// ----------------------------------------------------------------------------------------
+
+#constant BCUI_VIEW_INFO_SIZE := ( 1 + 1 + 1 ) // for the fields below
+#constant BCUI_VIEW_INFO_OFFSET_LINK    := 0
+#constant BCUI_VIEW_INFO_OFFSET_WIDGETS := 1
+#constant BCUI_VIEW_INFO_OFFSET_COLOR   := 2
+
+#constant BCUI_WIDGET_INFO_SIZE := ( 1 + 1 + 1 + 2 + 4 + 1 + 1 ) // for the fields below
+#constant BCUI_WIDGET_INFO_OFFSET_LINK  := 0
+#constant BCUI_WIDGET_INFO_OFFSET_OWNER := 1
+#constant BCUI_WIDGET_INFO_OFFSET_TYPE  := 2
+#constant BCUI_WIDGET_INFO_OFFSET_X     := 3
+#constant BCUI_WIDGET_INFO_OFFSET_Y     := 4
+#constant BCUI_WIDGET_INFO_OFFSET_RECT  := 5 // x1, y1, x2, y2
+#constant BCUI_WIDGET_INFO_OFFSET_COLOR := 9
+#constant BCUI_WIDGET_INFO_OFFSET_LABEL := 10
+
+#constant BCUI_WIDGET_BUTTON_HEIGHT := 26
+#constant BCUI_WIDGET_BUTTON_WIDTH  := 70
+#constant BCUI_WIDGET_BUTTON_CORNER :=  8
+
+#constant BCUI_WIDGET_ARROW_DIAMETER := 32
+#constant BCUI_WIDGET_ARROW_OFFSET_1 :=  8
+#constant BCUI_WIDGET_ARROW_OFFSET_2 := 10
+
+#constant BCUI_WIDGET_TEXT_HEIGHT := 26
+
+// ----------------------------------------------------------------------------------------
+
+var bcui_view_list; // linked list of view info structures
+var bcui_current_view; // active_view or 0 if no active view
+
+// ----------------------------------------------------------------------------------------
+
+func bcui_init()
+    bcui_view_list    := 0;
+    bcui_current_view := 0;
+endfunc
+
+// ----------------------------------------------------------------------------------------
+
+func bcui_widget_is_touchable( var widget )
+
+    switch ( widget[ BCUI_WIDGET_INFO_OFFSET_TYPE ] )
+
+        case BCUI_WIDGET_TYPE_BUTTON:
+        case BCUI_WIDGET_TYPE_ARROW:
+            return TRUE;
+
+    endswitch
+
+    return FALSE;
+
+endfunc
+
+// ----------------------------------------------------------------------------------------
+
+func bcui_pt_in_widget( var x, var y, var widget )
+    if ( x >= widget[ BCUI_WIDGET_INFO_OFFSET_RECT + 0 ]
+      && x <= widget[ BCUI_WIDGET_INFO_OFFSET_RECT + 2 ]
+      && y >= widget[ BCUI_WIDGET_INFO_OFFSET_RECT + 1 ]
+      && y <= widget[ BCUI_WIDGET_INFO_OFFSET_RECT + 3 ] )
+        return ( TRUE );
+    else
+        return ( FALSE );
+    endif
+endfunc
+
+// ----------------------------------------------------------------------------------------
+
+func bcui_darken( var color )
+    return ( ( ( color >> 12 ) & 0x0f ) << 11 )
+           | ( ( ( color >> 6 ) & 0x1f ) << 5 )
+       | ( ( color >> 1 ) & 0x0f );
+endfunc
+
+// ----------------------------------------------------------------------------------------
+
+func bcui_create_view( var background )
+
+    var private view;
+
+    view := mem_AllocZ( BCUI_VIEW_INFO_SIZE * 2 ); // 2x for bytes from words
+    view[ BCUI_VIEW_INFO_OFFSET_LINK ] := bcui_view_list;
+    bcui_view_list := view;
+
+    view[ BCUI_VIEW_INFO_OFFSET_WIDGETS ] := 0;
+    view[ BCUI_VIEW_INFO_OFFSET_COLOR ]   := background;
+
+    return view;
+
+endfunc
+
+// ----------------------------------------------------------------------------------------
+
+func bcui_create_button( var view, var center_x, var center_y, var label, var color )
+
+    var private button;
+
+    button := mem_AllocZ( BCUI_WIDGET_INFO_SIZE * 2 ); // 2x for bytes from words
+    button[ BCUI_WIDGET_INFO_OFFSET_LINK ] := view[ BCUI_VIEW_INFO_OFFSET_WIDGETS ];
+    view[ BCUI_VIEW_INFO_OFFSET_WIDGETS ]  := button;
+
+    button[ BCUI_WIDGET_INFO_OFFSET_OWNER ] := view;
+    button[ BCUI_WIDGET_INFO_OFFSET_TYPE ]  := BCUI_WIDGET_TYPE_BUTTON;
+
+    button[ BCUI_WIDGET_INFO_OFFSET_RECT + 0 ] := center_x - ( BCUI_WIDGET_BUTTON_WIDTH  / 2 );
+    button[ BCUI_WIDGET_INFO_OFFSET_RECT + 1 ] := center_y - ( BCUI_WIDGET_BUTTON_HEIGHT / 2 );
+    button[ BCUI_WIDGET_INFO_OFFSET_RECT + 2 ] := center_x + ( BCUI_WIDGET_BUTTON_WIDTH  / 2 );
+    button[ BCUI_WIDGET_INFO_OFFSET_RECT + 3 ] := center_y + ( BCUI_WIDGET_BUTTON_HEIGHT / 2 );
+
+    button[ BCUI_WIDGET_INFO_OFFSET_X ] := center_x;
+    button[ BCUI_WIDGET_INFO_OFFSET_Y ] := center_y;
+
+    button[ BCUI_WIDGET_INFO_OFFSET_COLOR ] := color;
+
+    button[ BCUI_WIDGET_INFO_OFFSET_LABEL ] := mem_AllocZ( str_Length( label ) + 1 );
+    str_Copy( str_Ptr( button[ BCUI_WIDGET_INFO_OFFSET_LABEL ] ), label );
+
+    return button;
+
+endfunc
+
+// ----------------------------------------------------------------------------------------
+
+func bcui_create_arrow( var view, var center_x, var center_y, var direction, var color )
+
+    var private arrow;
+
+    arrow := mem_AllocZ( BCUI_WIDGET_INFO_SIZE * 2 ); // 2x for bytes from words
+    arrow[ BCUI_WIDGET_INFO_OFFSET_LINK ] := view[ BCUI_VIEW_INFO_OFFSET_WIDGETS ];
+    view[ BCUI_VIEW_INFO_OFFSET_WIDGETS ] := arrow;
+
+    arrow[ BCUI_WIDGET_INFO_OFFSET_OWNER ] := view;
+    arrow[ BCUI_WIDGET_INFO_OFFSET_TYPE ]  := BCUI_WIDGET_TYPE_ARROW;
+
+    arrow[ BCUI_WIDGET_INFO_OFFSET_RECT + 0 ] := center_x - ( BCUI_WIDGET_ARROW_DIAMETER / 2 );
+    arrow[ BCUI_WIDGET_INFO_OFFSET_RECT + 1 ] := center_y - ( BCUI_WIDGET_ARROW_DIAMETER / 2 );
+    arrow[ BCUI_WIDGET_INFO_OFFSET_RECT + 2 ] := center_x + ( BCUI_WIDGET_ARROW_DIAMETER / 2 );
+    arrow[ BCUI_WIDGET_INFO_OFFSET_RECT + 3 ] := center_y + ( BCUI_WIDGET_ARROW_DIAMETER / 2 );
+
+    arrow[ BCUI_WIDGET_INFO_OFFSET_X ] := center_x;
+    arrow[ BCUI_WIDGET_INFO_OFFSET_Y ] := center_y;
+
+    arrow[ BCUI_WIDGET_INFO_OFFSET_LABEL ] := direction; // left, up, right, down
+
+    arrow[ BCUI_WIDGET_INFO_OFFSET_COLOR ] := color;
+
+    return arrow;
+
+endfunc
+
+// ----------------------------------------------------------------------------------------
+
+func bcui_do_touch() // returns object or 0 if no hit
+
+    if ( bcui_current_view == 0 )
+        return 0;
+    endif
+
+    var private widget;
+
+    widget := bcui_current_view[ BCUI_VIEW_INFO_OFFSET_WIDGETS ];
+
+    while ( widget != 0 )
+
+        var private touch_x, touch_y;
+
+        touch_x := touch_Get( TOUCH_GETX );
+        touch_y := touch_Get( TOUCH_GETY );
+
+        if ( bcui_widget_is_touchable( widget ) && bcui_pt_in_widget( touch_x, touch_y, widget ) )
+
+            bcui_paint_widget( widget, TRUE /* touched */ );
+
+            while ( touch_Get( TOUCH_STATUS ) != TOUCH_RELEASED )
+                pause( 10 );
+            wend
+
+            bcui_paint_widget( widget, FALSE /* not touched */ );
+            return widget;
+
+        endif
+
+        widget := widget[ BCUI_WIDGET_INFO_OFFSET_LINK ];
+
+    wend
+
+    // no match
+    return 0;
+
+endfunc
+
+// ----------------------------------------------------------------------------------------
+
+func bcui_activate_view( var view )
+
+    bcui_current_view := view;
+
+    if ( bcui_current_view == 0 )
+        gfx_RectangleFilled( 0, 0, gfx_Get( X_MAX ), gfx_Get( Y_MAX ), BLACK );
+        return;
+    endif
+
+    gfx_RectangleFilled( 0, 0,
+                         gfx_Get( X_MAX ), gfx_Get( Y_MAX ),
+                         view[ BCUI_VIEW_INFO_OFFSET_COLOR ] );
+
+    var private widget;
+
+    widget := view[ BCUI_VIEW_INFO_OFFSET_WIDGETS ];
+
+    while ( widget != 0 )
+
+        bcui_paint_widget( widget, FALSE /* not touched */ );
+
+        widget := widget[ BCUI_WIDGET_INFO_OFFSET_LINK ];
+
+    wend
+
+endfunc
+
+// ----------------------------------------------------------------------------------------
+
+func bcui_paint_widget( var widget, var as_touched )
+
+    var private widget_color;
+
+    if ( as_touched )
+        widget_color := bcui_darken( widget[ BCUI_WIDGET_INFO_OFFSET_COLOR ] );
+    else
+        widget_color := widget[ BCUI_WIDGET_INFO_OFFSET_COLOR ];
+    endif
+
+    switch ( widget[ BCUI_WIDGET_INFO_OFFSET_TYPE ] )
+
+        case BCUI_WIDGET_TYPE_BUTTON:
+
+            bcui_paint_button( widget[ BCUI_WIDGET_INFO_OFFSET_X ],
+                               widget[ BCUI_WIDGET_INFO_OFFSET_Y ],
+                               widget_color,
+                               widget[ BCUI_WIDGET_INFO_OFFSET_LABEL ] );
+            break;
+
+        case BCUI_WIDGET_TYPE_ARROW:
+
+            bcui_paint_arrow( widget[ BCUI_WIDGET_INFO_OFFSET_X ],
+                              widget[ BCUI_WIDGET_INFO_OFFSET_Y ],
+                              widget_color,
+                              widget[ BCUI_WIDGET_INFO_OFFSET_LABEL ] );
+            break;
+
+    endswitch
+
+endfunc
+
+// ----------------------------------------------------------------------------------------
+
+func bcui_paint_button( var center_x, var center_y, var color, var label )
+
+    // paint the four corners
+    gfx_CircleFilled( center_x - ( ( BCUI_WIDGET_BUTTON_WIDTH  / 2 ) - BCUI_WIDGET_BUTTON_CORNER ),
+                      center_y - ( ( BCUI_WIDGET_BUTTON_HEIGHT / 2 ) - BCUI_WIDGET_BUTTON_CORNER ),
+                      BCUI_WIDGET_BUTTON_CORNER, color );
+    gfx_CircleFilled( center_x - ( ( BCUI_WIDGET_BUTTON_WIDTH  / 2 ) - BCUI_WIDGET_BUTTON_CORNER ),
+                      center_y + ( ( BCUI_WIDGET_BUTTON_HEIGHT / 2 ) - BCUI_WIDGET_BUTTON_CORNER ),
+                      BCUI_WIDGET_BUTTON_CORNER, color );
+    gfx_CircleFilled( center_x + ( ( BCUI_WIDGET_BUTTON_WIDTH  / 2 ) - BCUI_WIDGET_BUTTON_CORNER ),
+                      center_y - ( ( BCUI_WIDGET_BUTTON_HEIGHT / 2 ) - BCUI_WIDGET_BUTTON_CORNER ),
+                      BCUI_WIDGET_BUTTON_CORNER, color );
+    gfx_CircleFilled( center_x + ( ( BCUI_WIDGET_BUTTON_WIDTH  / 2 ) - BCUI_WIDGET_BUTTON_CORNER ),
+                      center_y + ( ( BCUI_WIDGET_BUTTON_HEIGHT / 2 ) - BCUI_WIDGET_BUTTON_CORNER ),
+                      BCUI_WIDGET_BUTTON_CORNER, color );
+
+    // paint the filled rectanges (center, left, right )
+    gfx_RectangleFilled( center_x - ( ( BCUI_WIDGET_BUTTON_WIDTH / 2 ) - BCUI_WIDGET_BUTTON_CORNER ),
+                         center_y - ( BCUI_WIDGET_BUTTON_HEIGHT / 2 ),
+                         center_x + ( ( BCUI_WIDGET_BUTTON_WIDTH / 2 ) - BCUI_WIDGET_BUTTON_CORNER ),
+                         center_y + ( BCUI_WIDGET_BUTTON_HEIGHT / 2 ),
+                         color );
+    gfx_RectangleFilled( center_x - ( BCUI_WIDGET_BUTTON_WIDTH / 2 ),
+                         center_y - ( ( BCUI_WIDGET_BUTTON_HEIGHT / 2 ) - BCUI_WIDGET_BUTTON_CORNER ),
+                         center_x - ( ( BCUI_WIDGET_BUTTON_WIDTH / 2 ) - BCUI_WIDGET_BUTTON_CORNER ),
+                         center_y + ( ( BCUI_WIDGET_BUTTON_HEIGHT / 2 ) - BCUI_WIDGET_BUTTON_CORNER ),
+                         color );
+    gfx_RectangleFilled( center_x + ( ( BCUI_WIDGET_BUTTON_WIDTH  / 2 ) - BCUI_WIDGET_BUTTON_CORNER ),
+                         center_y - ( ( BCUI_WIDGET_BUTTON_HEIGHT / 2 ) - BCUI_WIDGET_BUTTON_CORNER ),
+                         center_x + ( BCUI_WIDGET_BUTTON_WIDTH / 2 ),
+                         center_y + ( ( BCUI_WIDGET_BUTTON_HEIGHT / 2 ) - BCUI_WIDGET_BUTTON_CORNER ),
+                         color );
+
+    // paint the label
+    txt_Set( FONT_ID, hFont1 );
+    txt_Set( TEXT_COLOUR, WHITE );
+    txt_Set( TEXT_HIGHLIGHT, color );
+    var private w, h;
+    w := strwidth( label );
+    h := strheight();
+    gfx_MoveTo( center_x - ( w / 2 ), center_y - ( h / 2 ) );
+    putstr( label );
+
+endfunc
+
+// ----------------------------------------------------------------------------------------
+
+func bcui_paint_arrow( var center_x, var center_y, var color, var direction )
+
+    // paint the circle itself
+    gfx_CircleFilled( center_x, center_y, ( BCUI_WIDGET_ARROW_DIAMETER / 2 ), color );
+
+    // paint the arrow inside, pointing in the right direction
+    switch ( direction )
+
+        case BCUI_WIDGET_ARROW_LEFT:
+
+            gfx_Triangle( ( center_x - BCUI_WIDGET_ARROW_OFFSET_2 ),
+                          center_y,
+                          ( center_x + ( BCUI_WIDGET_ARROW_OFFSET_2 / 2 ) ),
+                          ( center_y - BCUI_WIDGET_ARROW_OFFSET_1 ),
+                          ( center_x + ( BCUI_WIDGET_ARROW_OFFSET_2 / 2 ) ),
+                          ( center_y + BCUI_WIDGET_ARROW_OFFSET_1 ),
+                          WHITE );
+            break;
+
+        case BCUI_WIDGET_ARROW_UP:
+
+            gfx_Triangle( center_x,
+                          ( center_y - BCUI_WIDGET_ARROW_OFFSET_2 ),
+                          ( center_x - BCUI_WIDGET_ARROW_OFFSET_1 ),
+                          ( center_y + ( BCUI_WIDGET_ARROW_OFFSET_2 / 2 ) ),
+                          ( center_x + BCUI_WIDGET_ARROW_OFFSET_1 ),
+                          ( center_y + ( BCUI_WIDGET_ARROW_OFFSET_2 / 2 ) ),
+                          WHITE );
+            break;
+
+        case BCUI_WIDGET_ARROW_RIGHT:
+
+            gfx_Triangle( ( center_x + BCUI_WIDGET_ARROW_OFFSET_2 ),
+                          center_y,
+                          ( center_x - ( BCUI_WIDGET_ARROW_OFFSET_2 / 2 ) ),
+                          ( center_y - BCUI_WIDGET_ARROW_OFFSET_1 ),
+                          ( center_x - ( BCUI_WIDGET_ARROW_OFFSET_2 / 2 ) ),
+                          ( center_y + BCUI_WIDGET_ARROW_OFFSET_1 ),
+                          WHITE );
+            break;
+
+        case BCUI_WIDGET_ARROW_DOWN:
+
+            gfx_Triangle( center_x,
+                          ( center_y + BCUI_WIDGET_ARROW_OFFSET_2 ),
+                          ( center_x - BCUI_WIDGET_ARROW_OFFSET_1 ),
+                          ( center_y - ( BCUI_WIDGET_ARROW_OFFSET_2 / 2 ) ),
+                          ( center_x + BCUI_WIDGET_ARROW_OFFSET_1 ),
+                          ( center_y - ( BCUI_WIDGET_ARROW_OFFSET_2 / 2 ) ),
+                          WHITE );
+            break;
+
+    endswitch
+
+endfunc
+
+// ----------------------------------------------------------------------------------------
+
+func bcui_text_left( var text, var x, var y, var font, var color, var bkgnd )
+
+    // compute the metrics of the resulting string
+    txt_Set( FONT_ID, font );
+    txt_Set( TEXT_COLOUR, color );
+    txt_Set( TEXT_HIGHLIGHT, bkgnd );
+    var private w, h;
+    w := strwidth( text );
+    h := strheight();
+    // move to the right x,y location for left alignment
+    gfx_MoveTo( x, y - ( h / 2 ) );
+    // and paint the string
+    putstr( text );
+
+endfunc
+
+// ----------------------------------------------------------------------------------------
+
+func bcui_text_center( var text, var x, var y, var font, var color, var bkgnd )
+
+    // compute the metrics of the resulting string
+    txt_Set( FONT_ID, font );
+    txt_Set( TEXT_COLOUR, color );
+    txt_Set( TEXT_HIGHLIGHT, bkgnd );
+    var private w, h;
+    w := strwidth( text );
+    h := strheight();
+    // move to the right x,y location for center alignment
+    gfx_MoveTo( x - ( w / 2 ), y - ( h / 2 ) );
+    // and paint the string
+    putstr( text );
+
+endfunc
+
+// ----------------------------------------------------------------------------------------
+
+func bcui_text_right( var text, var x, var y, var font, var color, var bkgnd )
+
+    // compute the metrics of the resulting string
+    txt_Set( FONT_ID, font );
+    txt_Set( TEXT_COLOUR, color );
+    txt_Set( TEXT_HIGHLIGHT, bkgnd );
+    var private w, h;
+    w := strwidth( text );
+    h := strheight();
+    // move to the right x,y location for right alignment
+    gfx_MoveTo( x - w, y - ( h / 2 ) );
+    // and paint the string
+    putstr( text );
+
+endfunc
+
+// ----------------------------------------------------------------------------------------
--- a/main/robots/odr-display-4d/odr-display-4d.4dg	Sun Jan 05 16:52:45 2014 -0800
+++ b/main/robots/odr-display-4d/odr-display-4d.4dg	Sat Jan 11 18:00:27 2014 -0800
@@ -1,169 +1,214 @@
-#platform "uLCD-32PTU"
-
-#inherit "4DGL_16bitColours.fnc"
-
-#constant FALSE := 0
-#constant TRUE  := 1
-
-#inherit "bcui.inc"
-
-var disk, hFont1;
-
-func odrd_init()
-    var private buffer;
-    buffer := mem_AllocZ( 256 );
-    com_Init( buffer, 256, 0 /* no qualifier */ );
-endfunc
-
-#constant ODRD_COMMAND_MARKER := '|'
-
-#constant ODRD_STATE_GET_MARKER  := 0
-#constant ODRD_STATE_GET_COMMAND := 1
-#constant ODRD_STATE_GET_MESSAGE := 2
-
-#constant ODRD_MAX_MESSAGE_SIZE := 12
-
-var odrd_state;
-var odrd_command;
-var odrd_msgsize;
-var odrd_msgrecv;
-var odrd_message[ ODRD_MAX_MESSAGE_SIZE ];
-
-func odrd_message_size( var command )
-
-    switch ( command )
-
-        case 'F': // motor speed (front), 2 digits
-            return 2;
-
-        case 'R': // motor speed (rear), 2 digits
-            return 2;
-
-    endswitch
-
-    return 0; // unknown command, no message bytes
-
-endfunc
-
-func odrd_do_command()
-
-endfunc
-
-func odrd_read()
-
-    if ( com_Full() && com_Count() == 0 )
-        com_Reset();
-        odrd_state := ODRD_STATE_GET_MARKER;
-    endif
-
-    var private msg_ptr, value;
-    msg_ptr := str_Ptr( odrd_message );
-
-    while ( com_Count() != 0 )
-
-        value := serin();
-
-        switch ( odrd_state )
-
-            case ODRD_STATE_GET_MARKER:
-                if ( value == ODRD_COMMAND_MARKER )
-                    odrd_state := ODRD_STATE_GET_COMMAND;
-                endif
-                break;
-
-            case ODRD_STATE_GET_COMMAND:
-                odrd_command := value;
-                odrd_msgsize := odrd_message_size( odrd_command );
-                odrd_msgrecv := 0;
-                odrd_state   := ODRD_STATE_GET_MESSAGE;
-                break;
-
-            case ODRD_STATE_GET_MESSAGE:
-                str_PutByte( msg_ptr + odrd_msgrecv, value );
-                odrd_msgrecv++;
-                break;
-
-        endswitch
-
-        // have a complete command?
-        if ( odrd_msgsize == odrd_msgrecv )
-            str_PutByte( msg_ptr + odrd_msgrecv, 0 ); // null termniator
-            odrd_do_command();
-            odrd_state := ODRD_STATE_GET_MARKER;
-        endif
-
-    wend
-
-endfunc
-
-func main()
-
-    gfx_ScreenMode( LANDSCAPE );
-    touch_Set( TOUCH_ENABLE );
-
-    bcui_init();
-
-    //var hFont1;
-    disk   := file_Mount();
-//    hFont1 := file_LoadImageControl("AddFonts.da1", "AddFonts.gc1", 1);
-    hFont1 := file_LoadImageControl( "tahoma15.da1", "tahoma15.gc1", 1 );
-
-
-    //gfx_RectangleFilled( 0, 0, 319, 239, WHITE );
-
-    //txt_FGcolour( WHITE );
-    //gfx_MoveTo( 10, 10 );
-    //print( "Hello World" );
-
-    //gfx_Button( 0, 50, 150, BLUE, WHITE, 0, 1, 1, "Button" );
-    //gfx_Button( 1, 50, 180, BLUE, WHITE, 0, 1, 1, "Button" );
-
-    var private theView, btn_Hello, btn_There;
-
-    theView := bcui_create_view( bcui_darken( GRAY ) ); //WHITE );
-
-    btn_Hello := bcui_create_button( theView, 50, 100, "Hello", BLUEVIOLET );
-    btn_There := bcui_create_button( theView, 50, 150, "There", CORNFLOWERBLUE );
-    bcui_create_button( theView, 50, 200, "One", BLUE );
-
-    bcui_create_arrow( theView, 150, 150, BCUI_WIDGET_ARROW_LEFT, GREEN );
-    bcui_create_arrow( theView, 220, 150, BCUI_WIDGET_ARROW_RIGHT, GREEN );
-    bcui_create_arrow( theView, 185, 115, BCUI_WIDGET_ARROW_UP, GREEN );
-    bcui_create_arrow( theView, 185, 185, BCUI_WIDGET_ARROW_DOWN, GREEN );
-
-    bcui_activate_view( theView );
-
-    gfx_RectangleFilled( 50, 50, 100, 70, GREEN );
-    gfx_RectangleFilled( 120, 50, 140, 70, RED );
-
-    gfx_Slider( 1, 250, 50, 280, 200, GRAY, 100, 60 );
-
-    var private x, y;
-    repeat
-
-        odrd_read();
-
-        if ( touch_Get( TOUCH_STATUS ) == TOUCH_PRESSED )
-            x := touch_Get( TOUCH_GETX );
-            y := touch_Get( TOUCH_GETY );
-            if ( x >= 50 && x <= 100 && y >= 50 && y <= 70 )
-                gfx_RectangleFilled( 120, 50, 140, 70, WHITE );
-                while ( touch_Get( TOUCH_STATUS ) != TOUCH_RELEASED )
-                    pause( 50 );
-                wend
-                gfx_RectangleFilled( 120, 50, 140, 70, RED );
-            else
-                var private btn;
-                btn := bcui_do_touch();
-                if ( btn == btn_Hello )
-                    file_PlayWAV( "CHIMES.WAV" );
-                else if ( btn == btn_There )
-                    file_PlayWAV( "magchime.wav" );
-                endif
-            endif
-        endif
-
-    forever
-
-endfunc
-
+#platform "uLCD-32PTU"
+
+#inherit "4DGL_16bitColours.fnc"
+
+#constant FALSE := 0
+#constant TRUE  := 1
+
+#inherit "bcui.inc"
+
+var disk, hFont1;
+
+#constant COMM_COMMAND_MARKER := '|'
+
+#constant COMM_STATE_GET_MARKER  := 0
+#constant COMM_STATE_GET_COMMAND := 1
+#constant COMM_STATE_GET_MESSAGE := 2
+
+#constant COMM_MAX_MESSAGE_SIZE := 12
+#constant COMM_RX_BUFFER_SIZE   := 64
+#constant COMM_TX_BUFFER_SIZE   := 12
+
+var comm_func_cmd_msg_size;
+var comm_func_cmd_handler;
+
+var comm_errors;
+
+var comm_state;
+var comm_command;
+var comm_msgsize;
+var comm_msgrecv;
+var comm_message[ COMM_MAX_MESSAGE_SIZE ];
+
+var comm_rx_buffer[ COMM_RX_BUFFER_SIZE ];
+var comm_tx_buffer[ COMM_TX_BUFFER_SIZE ];
+
+func comm_init( var func_cmd_msg_size, var func_cmd_handler )
+
+    comm_func_cmd_msg_size := func_cmd_msg_size;
+    comm_func_cmd_handler  := func_cmd_handler;
+
+    com_Init( comm_rx_buffer, COMM_RX_BUFFER_SIZE * 2 /* 2x for words to bytes */, 0 /* no qualifier */ );
+    setbaud( BAUD_115200 );
+
+    com_TXbuffer( comm_tx_buffer, COMM_TX_BUFFER_SIZE * 2 /* 2x for words to bytes */, 0 /* no pin */ );
+
+endfunc
+
+func comm_read()
+
+    // did the receive buffer overflow? reset if so
+    if ( com_Full() && com_Count() == 0 )
+        com_Reset();
+        comm_state := COMM_STATE_GET_MARKER;
+        comm_errors++;
+    endif
+
+    var private msg_ptr, value;
+    msg_ptr := str_Ptr( comm_message );
+
+    while ( com_Count() != 0 )
+
+        value := serin();
+
+        switch ( comm_state )
+
+            case COMM_STATE_GET_MARKER:
+                if ( value == COMM_COMMAND_MARKER )
+                    comm_state := COMM_STATE_GET_COMMAND;
+                endif
+                break;
+
+            case COMM_STATE_GET_COMMAND:
+                comm_command := value;
+                comm_msgsize := comm_func_cmd_msg_size( comm_command );
+                comm_msgrecv := 0;
+                comm_state   := COMM_STATE_GET_MESSAGE;
+                break;
+
+            case COMM_STATE_GET_MESSAGE:
+                str_PutByte( msg_ptr + comm_msgrecv, value );
+                comm_msgrecv++;
+                break;
+
+        endswitch
+
+        // have a complete command?
+        if ( comm_state == COMM_STATE_GET_MESSAGE )
+            if ( comm_msgsize == comm_msgrecv )
+                str_PutByte( msg_ptr + comm_msgrecv, 0 ); // null termniator
+                comm_func_cmd_handler( comm_command, comm_message);
+                comm_state := COMM_STATE_GET_MARKER;
+            endif
+        endif
+
+    wend
+
+endfunc
+
+#constant ORDR_FONT  := FONT1
+#constant ODRD_BKGND := 0x4208
+
+func odrd_get_cmd_msg_size( var command )
+
+    switch ( command )
+
+        case 'F': // motor speed set, front, sign and 2 digits
+        case 'f': // motor speed actual, front, sign and 2 digits
+            return 3;
+
+        case 'R': // motor speed set, rear, sign and 2 digits
+        case 'r': // motor speed actual, rear, sign and 2 digits
+            return 3;
+
+    endswitch
+
+    return 0; // unknown command, no message bytes
+
+endfunc
+
+func odrd_do_command( var command, var message )
+
+    switch ( command )
+
+        case 'F': // motor speed set, front
+            bcui_text_center( message, 185, 150, ORDR_FONT, WHITE, ODRD_BKGND );
+            break;
+
+        case 'f': // motor speed actual, front
+            bcui_text_left( message, 275, 200, ORDR_FONT, WHITE, ODRD_BKGND );
+            break;
+
+    endswitch
+
+endfunc
+
+func main()
+
+    gfx_ScreenMode( LANDSCAPE );
+    touch_Set( TOUCH_ENABLE );
+
+    bcui_init();
+
+    //var hFont1;
+    disk   := file_Mount();
+//    hFont1 := file_LoadImageControl("AddFonts.da1", "AddFonts.gc1", 1);
+    hFont1 := file_LoadImageControl( "tahoma15.da1", "tahoma15.gc1", 1 );
+
+
+    //gfx_RectangleFilled( 0, 0, 319, 239, WHITE );
+
+    //txt_FGcolour( WHITE );
+    //gfx_MoveTo( 10, 10 );
+    //print( "Hello World" );
+
+    var private theView, btn_Hello, btn_There;
+
+    theView := bcui_create_view( ODRD_BKGND );
+
+    btn_Hello := bcui_create_button( theView, 50, 100, "Hello", BLUEVIOLET );
+    btn_There := bcui_create_button( theView, 50, 150, "There", CORNFLOWERBLUE );
+    bcui_create_button( theView, 50, 200, "One", BLUE );
+
+    bcui_create_arrow( theView, 150, 150, BCUI_WIDGET_ARROW_LEFT, GREEN );
+    bcui_create_arrow( theView, 220, 150, BCUI_WIDGET_ARROW_RIGHT, GREEN );
+    bcui_create_arrow( theView, 185, 115, BCUI_WIDGET_ARROW_UP, GREEN );
+    bcui_create_arrow( theView, 185, 185, BCUI_WIDGET_ARROW_DOWN, GREEN );
+
+    bcui_activate_view( theView );
+
+    bcui_text_right( "Actual:", 270, 200, ORDR_FONT, WHITE, ODRD_BKGND );
+    bcui_text_left( "000", 275, 200, ORDR_FONT, WHITE, ODRD_BKGND );
+
+    bcui_text_center( "---", 185, 150, ORDR_FONT, WHITE, ODRD_BKGND );
+
+    gfx_RectangleFilled( 50, 50, 100, 70, GREEN );
+    gfx_RectangleFilled( 120, 50, 140, 70, RED );
+
+    comm_init( odrd_get_cmd_msg_size, odrd_do_command );
+
+    var private x, y;
+    repeat
+
+        comm_read();
+
+        txt_Set( FONT_ID, ORDR_FONT );
+        gfx_MoveTo( 5, 10 );
+        putnum( DEC, mem_Heap() );
+        gfx_MoveTo( 70, 10 );
+        putnum( DEC, comm_errors );
+
+        if ( touch_Get( TOUCH_STATUS ) == TOUCH_PRESSED )
+            x := touch_Get( TOUCH_GETX );
+            y := touch_Get( TOUCH_GETY );
+            if ( x >= 50 && x <= 100 && y >= 50 && y <= 70 )
+                gfx_RectangleFilled( 120, 50, 140, 70, WHITE );
+                while ( touch_Get( TOUCH_STATUS ) != TOUCH_RELEASED )
+                    pause( 50 );
+                wend
+                gfx_RectangleFilled( 120, 50, 140, 70, RED );
+            else
+                var private btn;
+                btn := bcui_do_touch();
+                if ( btn == btn_Hello )
+                    file_PlayWAV( "CHIMES.WAV" );
+                else if ( btn == btn_There )
+                    file_PlayWAV( "magchime.wav" );
+                endif
+            endif
+        endif
+
+    forever
+
+endfunc
+
--- a/main/robots/odr-display-4d/odr-display-4d.cfg	Sun Jan 05 16:52:45 2014 -0800
+++ b/main/robots/odr-display-4d/odr-display-4d.cfg	Sat Jan 11 18:00:27 2014 -0800
@@ -1,1 +1,1 @@
-uLCD-32PTU,2,Picaso,uLCD-32PTU-A
+uLCD-32PTU,2,Picaso,uLCD-32PTU-A