/*

*/

/****************************************************************************
    This file is part of the FreeWRL/FreeX3D Distribution.

    Copyright 2009 CRC Canada. (http://www.crc.gc.ca)

    FreeWRL/FreeX3D is free software: you can redistribute it and/or modify
    it under the terms of the GNU Lesser Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    FreeWRL/FreeX3D is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with FreeWRL/FreeX3D.  If not, see <http://www.gnu.org/licenses/>.
****************************************************************************/


#include <config.h>
#include <system.h>
#include <internal.h>

#include <libFreeWRL.h>
#include <scenegraph/Viewer.h>
#include <opengl/Textures.h>
#include <opengl/LoadTextures.h>
#include "scenegraph/RenderFuncs.h"

/* the following are bitmap icons for the toolbar, 
generated by writing out C structs from thresholded png icons. Setting 2
parameters in this statusbarHud.c causes it to read in your 32x32xRGBA .pngs.
and write out C struct versions: 
buttonType = 0; // 0 = rgba .png 1= .c bitmap (see above) 
savePng2dotc = 1; // if you read png and want to save to a bitmap .c struct, put 1 
*/ 
#if defined(STATUSBAR_HUD)
//#define KIOSK 1
//#define TOUCH 1

/*the colors listed here are for a default. But are over-ridden now by colors and default 
	listed in common.c in freewrl
*/
// StatusbarHud color schemes:
//#define OLDCOLORS 1
//#define MIDNIGHT 1
//#define ANGRY 1
//#define FREEWRLICO 1
//#define AQUA 1
#define NEON 1
static GLfloat colorCursor[4]			= {.7f,.7f,.9f,1.0f};		//sidebyside stereo eyebase cursor (is this still used, or is there something in mainloop.c now?)
static GLfloat colorButtonHighlight[4]	= {.5f,.5f,.5f,.5f};
static GLfloat colorButtonCTRL[4]		= {.6f,.6f,.6f,.5f};

#ifdef OLDCOLORS
static GLfloat colorClear[4]			= {.922f,.91f,.844f,1.0f};  //offwhite
static GLfloat colorButtonIcon[4]		= {0.37f,0.37f,0.9f,1.0f};  //medium blue
static GLfloat colorStatusbarText[4]	= {.2f, .2f, .2f, 1.0f};	//very dark grey
static GLfloat colorMessageText[4]		= {1.0f, 1.0f, 1.0f, 1.0f}; //white
#elif MIDNIGHT 
static GLfloat colorClear[4]			= {0.0f,0.0f,0.0f,1.0f};  //bleck
static GLfloat colorButtonIcon[4]		= {1.f,1.f,1.0f,1.0f}; //white
static GLfloat colorStatusbarText[4]	= {1.0f, 1.0f, 1.0f, 1.0f}; //white
static GLfloat colorMessageText[4]		= {1.0f, 1.0f, 1.0f, 1.0f}; //white
#elif ANGRY 
static GLfloat colorClear[4]			= {0.0f,0.2f,0.2f,1.0f};  //slightly blue-green black
static GLfloat colorButtonIcon[4]		= {1.0f, 0.0f, 0.0f, 1.0f}; //red
static GLfloat colorStatusbarText[4]	= {1.0f, 0.0f, 0.0f, 1.0f}; //red
static GLfloat colorMessageText[4]		= {1.0f, 0.0f, 0.0f, 1.0f}; //red
#elif FREEWRLICO 
static GLfloat colorClear[4]			= {0.0f,0.25f,0.45f,1.0f}; //indigo
static GLfloat colorButtonIcon[4]		= {.57f, 0.8f, 0.94f, 1.0f}; //light aqua
static GLfloat colorStatusbarText[4]	= {1.0f, 0.47f, 0.0f, 1.0f}; //orange
static GLfloat colorMessageText[4]		= {1.0f, 0.47f, 0.0f, 1.0f}; //orange
#elif AQUA 
static GLfloat colorClear[4]			= {0.75f,0.83f,0.74f,1.0f}; //clamshell
static GLfloat colorButtonIcon[4]		= {.0f, 0.44f, 0.52f, 1.0f};  //dark aqua/indigo
static GLfloat colorStatusbarText[4]	= {.32f, 0.45f, 0.43f, 1.0f};  //dark clamshell
static GLfloat colorMessageText[4]		= {.06f, 0.69f, 0.8f, 1.0f}; //aqua
//which is often black
#elif NEON
static GLfloat colorClear[4]			= {0.24f,0.27f,0.34f,1.0f};  //steely grey
#define LIME {.8f,1.0f,0.0f,1.0f}
#define YELLOW {1.0f,1.0f,.2f,1.0f}
#define CYAN {0.0f,1.0f,1.0f,1.0f}
#define PINK {1.0f,.47f,1.0f,1.0f}
#define HIGHLIGHT LIME
static GLfloat colorButtonIcon[4]		= HIGHLIGHT;
static GLfloat colorStatusbarText[4]	= HIGHLIGHT;
static GLfloat colorMessageText[4]		= HIGHLIGHT; //over VRML window, which is often black
#endif

static int ui_color_changed = -1;

void update_ui_colors(){
	int ic;
	ic = fwl_get_ui_color_changed();
	if( ic != ui_color_changed){
		fwl_get_ui_color("panel",colorClear);
		fwl_get_ui_color("menuIcon",colorButtonIcon);
		fwl_get_ui_color("statusText",colorStatusbarText);
		fwl_get_ui_color("messageText",colorMessageText);
		ui_color_changed = ic;
	}
}
static   GLbyte vShaderStr[] =  
      "attribute vec4 a_position;   \n"
      "attribute vec2 a_texCoord;   \n"
      "varying vec2 v_texCoord;     \n"
      "void main()                  \n"
      "{                            \n"
      "   gl_Position = a_position; \n"
      "   v_texCoord = a_texCoord;  \n"
      "}                            \n";

// using Luminance-alpha images, you need to set a color in order for it to show up different than white
static   GLbyte fShaderStr[] =  
#ifdef GL_ES_VERSION_2_0
      "precision mediump float;                            \n"
#endif //GL_ES_VERSION_2_0
      "varying vec2 v_texCoord;                            \n"
      "uniform sampler2D Texture0;                         \n"
      "uniform vec4 Color4f;                               \n"
      "void main()                                         \n"
      "{                                                   \n"
      "  gl_FragColor = Color4f * texture2D( Texture0, v_texCoord ); \n"
      "}                                                   \n";
//	  "  gl_FragColor = vec4(1.0,1.0,1.0,1.0); \n"

GLuint esLoadShader ( GLenum type, const char *shaderSrc )
{
   GLuint shader;
   GLint compiled;
   // Create the shader object
   shader = glCreateShader ( type );

   if ( shader == 0 )
   	return 0;

   // Load the shader source
   glShaderSource ( shader, 1, &shaderSrc, NULL );
   
   // Compile the shader
   glCompileShader ( shader );

   // Check the compile status
   glGetShaderiv ( shader, GL_COMPILE_STATUS, &compiled );

   if ( !compiled ) 
   {
      GLint infoLen = 0;

      glGetShaderiv ( shader, GL_INFO_LOG_LENGTH, &infoLen );
      
      if ( infoLen > 1 )
      {
         char* infoLog = MALLOC(void *, sizeof(char) * infoLen );

         glGetShaderInfoLog ( shader, infoLen, NULL, infoLog );
         printf ( "Error compiling shader:\n%s\n", infoLog );            
         
         FREE( infoLog );
      }

      glDeleteShader ( shader );
      return 0;
   }

   return shader;

}

GLuint esLoadProgram ( const char *vertShaderSrc, const char *fragShaderSrc )
{
   GLuint vertexShader;
   GLuint fragmentShader;
   GLuint programObject;
   GLint linked;

   // Load the vertex/fragment shaders
   vertexShader = esLoadShader ( GL_VERTEX_SHADER, vertShaderSrc );
   if ( vertexShader == 0 )
      return 0;

   fragmentShader = esLoadShader ( GL_FRAGMENT_SHADER, fragShaderSrc );
   if ( fragmentShader == 0 )
   {
      glDeleteShader( vertexShader );
      return 0;
   }

   // Create the program object
   programObject = glCreateProgram ( );
   
   if ( programObject == 0 )
      return 0;

   glAttachShader ( programObject, vertexShader );
   glAttachShader ( programObject, fragmentShader );

   // Link the program
   glLinkProgram ( programObject );

   // Check the link status
   glGetProgramiv ( programObject, GL_LINK_STATUS, &linked );

   if ( !linked ) 
   {
      GLint infoLen = 0;

      glGetProgramiv ( programObject, GL_INFO_LOG_LENGTH, &infoLen );
      
      if ( infoLen > 1 )
      {
         char* infoLog = MALLOC(void *, sizeof(char) * infoLen );

         glGetProgramInfoLog ( programObject, infoLen, NULL, infoLog );
         printf ( "Error linking program:\n%s\n", infoLog );            
         
         FREE( infoLog );
      }

      glDeleteProgram ( programObject );
      return 0;
   }

   // Free up no longer needed shader resources
   glDeleteShader ( vertexShader );
   glDeleteShader ( fragmentShader );

   return programObject;
}


//#include "hudIcons_hexbit.h" //2010 bit per pixel
#include "hudIcons_octalpha.h"  //2012 byte per pixel (nicer)

/* <<< bitmap menu button icons */

/*fw fixed size bitmap fonts >>>
first 1: char # 0-127 (ie '!'=33, 'a'=97)
next 6: FW_GL_BITMAP(width,height,xbo,ybo,xadv,yadv,
last 7+: FW_GL_BITMAP(,,,,,,const GLubyte *bitmap);
Non-ASCII chars:
[ ] dec 28 oct 034
[*] dec 29 oct 035
<*  dec 30 oct 036
*>  dec 31 oct 037
*/


GLubyte fwLetters8x15[][22] = {
{28,8,15,0,0,8,0,0x0,0x0,0x0,0xfe,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0xfe,0x0,0x0,0x0},
{29,8,15,0,0,8,0,0x0,0x0,0x0,0xfe,0x82,0x92,0xba,0xca,0x8a,0x86,0x86,0xfe,0x4,0x2,0x2},
{30,8,15,0,0,8,0,0x0,0x0,0x0,0x4,0xc,0x1c,0x3c,0x7c,0xfc,0x7c,0x3c,0x1c,0xc,0x4,0x0},
{31,8,15,0,0,8,0,0x0,0x0,0x0,0x80,0xc0,0xe0,0xf0,0xf8,0xfc,0xf8,0xf0,0xe0,0xc0,0x80,0x0},
{32,8,15,0,0,8,0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0},
{33,8,15,0,0,8,0,0x0,0x0,0x0,0x0,0x20,0x20,0x0,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x0},
{35,8,15,0,0,8,0,0x0,0x0,0x0,0x0,0x24,0x24,0x24,0xfe,0x24,0x24,0x24,0xfe,0x24,0x0,0x0},
{36,8,15,0,0,8,0,0x0,0x0,0x0,0x10,0x38,0x54,0x94,0x14,0x18,0x10,0x70,0x90,0x94,0x78,0x10},
{37,8,15,0,0,8,0,0x0,0x0,0x0,0x80,0x44,0x4a,0x2a,0x34,0x10,0x10,0x48,0xa8,0xa4,0x44,0x0},
{38,8,15,0,0,8,0,0x0,0x0,0x0,0x0,0x74,0x88,0x94,0xa0,0x40,0x40,0xa0,0x90,0x50,0x20,0x0},
{39,8,15,0,0,8,0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x40,0x20,0x20,0x30,0x30,0x0,0x0},
{40,8,15,0,0,8,0,0x0,0x0,0x0,0x8,0x10,0x20,0x20,0x40,0x40,0x40,0x40,0x20,0x20,0x10,0x8},
{41,8,15,0,0,8,0,0x0,0x0,0x40,0x20,0x10,0x10,0x8,0x8,0x8,0x8,0x8,0x10,0x10,0x20,0x40},
{42,8,15,0,0,8,0,0x0,0x0,0x0,0x0,0x0,0x0,0x10,0x54,0x38,0x38,0x54,0x10,0x0,0x0,0x0},
{43,8,15,0,0,8,0,0x0,0x0,0x0,0x0,0x10,0x10,0x10,0xfe,0x10,0x10,0x10,0x10,0x0,0x0,0x0},
{44,8,15,0,0,8,0,0x0,0x0,0x20,0x10,0x18,0x18,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0},
{45,8,15,0,0,8,0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf8,0x0,0x0,0x0,0x0,0x0,0x0},
{46,8,15,0,0,8,0,0x0,0x0,0x0,0x0,0x30,0x30,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0},
{47,8,15,0,0,8,0,0x0,0x0,0x40,0x40,0x20,0x20,0x10,0x10,0x8,0x8,0x4,0x4,0x2,0x2,0x0},
{48,8,15,0,0,8,0,0x0,0x0,0x0,0x0,0x78,0x84,0x84,0xc4,0xa4,0x9c,0x84,0x84,0x84,0x78,0x0},
{49,8,15,0,0,8,0,0x0,0x0,0x0,0x0,0x38,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x30,0x10,0x0},
{50,8,15,0,0,8,0,0x0,0x0,0x0,0x0,0xfe,0x80,0x40,0x20,0x10,0x8,0x6,0x82,0x82,0x7c,0x0},
{51,8,15,0,0,8,0,0x0,0x0,0x0,0x0,0x78,0x84,0x4,0x4,0x4,0x18,0x4,0x4,0x84,0x78,0x0},
{52,8,15,0,0,8,0,0x0,0x0,0x0,0x0,0x8,0x8,0x8,0x8,0xfc,0x88,0x48,0x28,0x18,0x8,0x0},
{53,8,15,0,0,8,0,0x0,0x0,0x0,0x0,0x78,0x84,0x4,0x4,0x84,0xf8,0x80,0x80,0x80,0xfc,0x0},
{54,8,15,0,0,8,0,0x0,0x0,0x0,0x0,0x7c,0x84,0x82,0xc2,0xa4,0x98,0x80,0x84,0x44,0x38,0x0},
{55,8,15,0,0,8,0,0x0,0x0,0x0,0x0,0x20,0x20,0x10,0x10,0x10,0x10,0x8,0x4,0x4,0xfc,0x0},
{56,8,15,0,0,8,0,0x0,0x0,0x0,0x0,0x78,0x84,0x84,0x84,0x84,0x84,0x78,0x84,0x84,0x78,0x0},
{57,8,15,0,0,8,0,0x0,0x0,0x0,0x0,0x78,0x84,0x4,0x34,0x4c,0x84,0x84,0x84,0x44,0x38,0x0},
{58,8,15,0,0,8,0,0x0,0x0,0x0,0x0,0x30,0x30,0x0,0x0,0x0,0x30,0x30,0x0,0x0,0x0,0x0},
{59,8,15,0,0,8,0,0x0,0x40,0x20,0x10,0x30,0x30,0x0,0x0,0x30,0x30,0x0,0x0,0x0,0x0,0x0},
{60,8,15,0,0,8,0,0x0,0x0,0x0,0x4,0x8,0x10,0x20,0x40,0x80,0x40,0x20,0x10,0x8,0x4,0x0},
{61,8,15,0,0,8,0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf8,0x0,0x0,0xf8,0x0,0x0,0x0,0x0},
{62,8,15,0,0,8,0,0x0,0x0,0x80,0x40,0x20,0x10,0x8,0x4,0x4,0x8,0x10,0x20,0x40,0x80,0x0},
{63,8,15,0,0,8,0,0x0,0x0,0x0,0x10,0x10,0x0,0x0,0x10,0x18,0x4,0x2,0x82,0x44,0x38,0x0},
{64,8,15,0,0,8,0,0x0,0x0,0x0,0x0,0x38,0x44,0x80,0x98,0xa4,0xa4,0x9c,0x84,0x48,0x30,0x0},
{65,8,15,0,0,8,0,0x0,0x0,0x0,0x0,0x84,0x84,0xfc,0x84,0x48,0x48,0x48,0x30,0x30,0x0,0x0},
{66,8,15,0,0,8,0,0x0,0x0,0x0,0x0,0xf8,0x84,0x84,0x84,0x84,0xf8,0x84,0x84,0x84,0xf8,0x0},
{67,8,15,0,0,8,0,0x0,0x0,0x0,0x0,0x78,0x84,0x80,0x80,0x80,0x80,0x80,0x80,0x84,0x78,0x0},
{68,8,15,0,0,8,0,0x0,0x0,0x0,0x0,0xf0,0x88,0x84,0x84,0x84,0x84,0x84,0x88,0xf0,0x0,0x0},
{69,8,15,0,0,8,0,0x0,0x0,0x0,0x0,0xfc,0x80,0x80,0x80,0x80,0xf0,0x80,0x80,0xfc,0x0,0x0},
{70,8,15,0,0,8,0,0x0,0x0,0x0,0x0,0x80,0x80,0x80,0x80,0x80,0xf0,0x80,0x80,0x80,0xfe,0x0},
{71,8,15,0,0,8,0,0x0,0x0,0x0,0x0,0x7a,0x86,0x82,0x82,0x82,0x8c,0x80,0x80,0x44,0x38,0x0},
{72,8,15,0,0,8,0,0x0,0x0,0x0,0x0,0x84,0x84,0x84,0x84,0xfc,0x84,0x84,0x84,0x84,0x84,0x0},
{73,8,15,0,0,8,0,0x0,0x0,0x0,0x0,0x38,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x38,0x0},
{74,8,15,0,0,8,0,0x0,0x0,0x0,0x0,0x70,0x88,0x88,0x8,0x8,0x8,0x8,0x8,0x8,0x18,0x0},
{75,8,15,0,0,8,0,0x0,0x0,0x0,0x0,0x86,0x88,0x90,0xa0,0xc0,0xa0,0x90,0x88,0x84,0x80,0x0},
{76,8,15,0,0,8,0,0x0,0x0,0x0,0x0,0xfc,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x0},
{77,8,15,0,0,8,0,0x0,0x0,0x0,0x0,0x82,0x82,0x92,0x92,0xaa,0xaa,0xc6,0xc6,0x82,0x82,0x0},
{78,8,15,0,0,8,0,0x0,0x0,0x0,0x0,0x84,0x8c,0x8c,0x94,0x94,0xa4,0xa4,0xc4,0xc4,0x84,0x0},
{79,8,15,0,0,8,0,0x0,0x0,0x0,0x0,0x78,0x84,0x84,0x84,0x84,0x84,0x84,0x84,0x84,0x78,0x0},
{80,8,15,0,0,8,0,0x0,0x0,0x0,0x0,0x80,0x80,0x80,0x80,0xb8,0xc4,0x84,0x84,0x84,0xf8,0x0},
{81,8,15,0,0,8,0,0x0,0x4,0x18,0x20,0x7c,0xa2,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x7c,0x0},
{82,8,15,0,0,8,0,0x0,0x0,0x0,0x0,0x82,0x84,0x8c,0x88,0xfc,0x82,0x82,0x82,0x82,0xfc,0x0},
{83,8,15,0,0,8,0,0x0,0x0,0x0,0x0,0x78,0x84,0x4,0x4,0x18,0x60,0x80,0x80,0x84,0x7c,0x0},
{84,8,15,0,0,8,0,0x0,0x0,0x0,0x0,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0xfe,0x0},
{85,8,15,0,0,8,0,0x0,0x0,0x0,0x0,0x7c,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x0},
{86,8,15,0,0,8,0,0x0,0x0,0x0,0x0,0x10,0x10,0x28,0x44,0x44,0x44,0x44,0x82,0x82,0x82,0x0},
{87,8,15,0,0,8,0,0x0,0x0,0x0,0x0,0x44,0x44,0xaa,0xaa,0x92,0x92,0x92,0x82,0x82,0x0,0x0},
{88,8,15,0,0,8,0,0x0,0x0,0x0,0x0,0x84,0x84,0x48,0x48,0x30,0x30,0x4c,0x44,0x84,0x84,0x0},
{89,8,15,0,0,8,0,0x0,0x0,0x0,0x0,0x10,0x10,0x10,0x10,0x10,0x28,0x28,0x44,0x82,0x82,0x0},
{90,8,15,0,0,8,0,0x0,0x0,0x0,0x0,0xfe,0x80,0x40,0x40,0x20,0x10,0x8,0x4,0x4,0xfe,0x0},
{91,8,15,0,0,8,0,0x0,0x0,0x0,0xe0,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0xe0,0x0},
{92,8,15,0,0,8,0,0x0,0x0,0x4,0x4,0x8,0x8,0x10,0x10,0x20,0x20,0x40,0x40,0x80,0x80,0x0},
{93,8,15,0,0,8,0,0x0,0x0,0x0,0x38,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x38,0x0},
{94,8,15,0,0,8,0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x44,0x28,0x10,0x0},
{95,8,15,0,0,8,0,0x0,0xfe,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0},
{96,8,15,0,0,8,0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20,0x40,0xc0,0xc0,0x0},
{97,8,15,0,0,8,0,0x0,0x0,0x0,0x0,0x74,0x88,0x98,0x68,0x8,0x88,0x70,0x0,0x0,0x0,0x0},
{98,8,15,0,0,8,0,0x0,0x0,0x0,0x0,0xb8,0xc4,0x84,0xc4,0xc4,0xb8,0x80,0x80,0x80,0x0,0x0},
{99,8,15,0,0,8,0,0x0,0x0,0x0,0x0,0x78,0x84,0x80,0x80,0x80,0x84,0x78,0x0,0x0,0x0,0x0},
{100,8,15,0,0,8,0,0x0,0x0,0x0,0x0,0x74,0x8c,0x8c,0x84,0x8c,0x74,0x4,0x4,0x4,0x0,0x0},
{101,8,15,0,0,8,0,0x0,0x0,0x0,0x0,0x78,0x84,0x80,0xbc,0xc4,0x84,0x78,0x0,0x0,0x0,0x0},
{102,8,15,0,0,8,0,0x0,0x0,0x0,0x0,0x20,0x20,0x20,0x20,0x78,0x20,0x20,0x24,0x3c,0x0,0x0},
{103,8,15,0,0,8,0,0x18,0x64,0x4,0x4,0x34,0x4c,0x84,0x84,0x84,0x8c,0x74,0x0,0x0,0x0,0x0},
{104,8,15,0,0,8,0,0x0,0x0,0x0,0x0,0x84,0x84,0x84,0x84,0x84,0xc4,0xb8,0x80,0x80,0x80,0x0},
{105,8,15,0,0,8,0,0x0,0x0,0x0,0x0,0x10,0x10,0x10,0x10,0x10,0x10,0x30,0x0,0x10,0x0,0x0},
{106,8,15,0,0,8,0,0x40,0xa0,0x90,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x30,0x0,0x10,0x0},
{107,8,15,0,0,8,0,0x0,0x0,0x0,0x0,0x84,0x98,0xb0,0xc0,0xa0,0x90,0x88,0x80,0x80,0x0,0x0},
{108,8,15,0,0,8,0,0x0,0x0,0x0,0x0,0x18,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x30,0x0,0x0},
{109,8,15,0,0,8,0,0x0,0x0,0x0,0x0,0x54,0x54,0x54,0x54,0x54,0x54,0xa8,0x0,0x0,0x0,0x0},
{110,8,15,0,0,8,0,0x0,0x0,0x0,0x0,0x84,0x84,0x84,0x84,0x84,0xc8,0xb8,0x0,0x0,0x0,0x0},
{111,8,15,0,0,8,0,0x0,0x0,0x0,0x0,0x78,0x84,0x84,0x84,0x84,0x84,0x78,0x0,0x0,0x0,0x0},
{112,8,15,0,0,8,0,0x80,0x80,0x80,0x80,0xb8,0xa4,0xc4,0x84,0x84,0xc4,0xa4,0x18,0x0,0x0,0x0},
{113,8,15,0,0,8,0,0x2,0x4,0x4,0x4,0x74,0x8c,0x8c,0x84,0x84,0x8c,0x74,0x0,0x0,0x0,0x0},
{114,8,15,0,0,8,0,0x0,0x0,0x0,0x0,0x80,0x80,0x80,0x80,0xc0,0xa4,0xb8,0x0,0x0,0x0,0x0},
{115,8,15,0,0,8,0,0x0,0x0,0x0,0x0,0xf8,0x84,0x4,0x38,0x40,0x84,0x78,0x0,0x0,0x0,0x0},
{116,8,15,0,0,8,0,0x0,0x0,0x0,0x0,0x30,0x28,0x20,0x20,0x20,0x20,0x78,0x20,0x20,0x0,0x0},
{117,8,15,0,0,8,0,0x0,0x0,0x0,0x0,0x74,0x4c,0x84,0x84,0x84,0x84,0x84,0x0,0x0,0x0,0x0},
{118,8,15,0,0,8,0,0x0,0x0,0x0,0x0,0x30,0x30,0x48,0x48,0x84,0x84,0x84,0x0,0x0,0x0,0x0},
{119,8,15,0,0,8,0,0x0,0x0,0x0,0x0,0x24,0x5a,0x92,0x92,0x82,0x82,0x82,0x0,0x0,0x0,0x0},
{120,8,15,0,0,8,0,0x0,0x0,0x0,0x0,0x84,0x84,0x48,0x30,0x48,0x84,0x84,0x0,0x0,0x0,0x0},
{121,8,15,0,0,8,0,0x38,0x44,0x84,0x4,0x74,0x8c,0x84,0x84,0x84,0x84,0x0,0x0,0x0,0x0,0x0},
{122,8,15,0,0,8,0,0x0,0x0,0x0,0x0,0xfc,0x80,0x40,0x20,0x10,0x8,0xfc,0x0,0x0,0x0,0x0},
{123,8,15,0,0,8,0,0x0,0x0,0x30,0x40,0x40,0x40,0x40,0x40,0xc0,0x40,0x40,0x40,0x40,0x30,0x0},
{124,8,15,0,0,8,0,0x0,0x0,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x0},
{125,8,15,0,0,8,0,0x0,0x0,0x0,0x60,0x10,0x10,0x10,0x10,0x18,0x10,0x10,0x10,0x10,0x60,0x0},
{126,8,15,0,0,8,0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x98,0xb4,0x64,0x0,0x0,0x0,0x0,0x0},
{255,0,0,0,0,0,0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0}
};
//the buttons need to be larger for fingers on touch devices
#if defined(QNX) || defined(KIOSK)
#define BUTSIZE 48
#else
#define BUTSIZE 32
#endif
/* <<< bitmap fonts */
typedef struct {
	int cwidth;
	int cheight;
	int have[256];
	GLfloat tex[2][2][256];  //texture coordinates lower left, upper right
	GLfloat owh[2][2][256];  //offset, width, height
	GLubyte *lumalpha;
	GLuint textureID;
} pfont_t;

typedef struct buttonSet buttonSet;
typedef struct {
	int width;
	int height;
	GLfloat tex0[2][2];
	GLfloat owh[2][2];
	//GLfloat vert[12];
	GLfloat tex[8];
	GLubyte *lumalpha;
	char *name;
	int action; //ACTION_
	int butStatus;
	bool isToggle;
	bool isRadio;
	int *radioset;
	buttonSet *buttonset;
} pmenuItem_t;

typedef struct buttonSet {
	int n;
	int index;
	pmenuItem_t ** items;
} buttonSet;
//Mar 2015 separate menubar from list of menuitems
//  menuitmes - icons and actions which are prepared and can be placed on a menubar
//  menubar - (new) container holding a runtime-changable arrangement of menuitems
//    - benefit: FLY2 > dragchords > {yawz,xy,yawpitch,roll} can share one menubar button, 
//      with 2 click modes: a) change FLY2 chord (a toggle mode, new) and b) switch to FLY2 from another (normal)

typedef struct {
	pmenuItem_t *item;   //holds icon specifics, and meaning: Action
	GLfloat vert[12];	//bar designed coordinates
	int action; //over-ride of the menuitem action if needed
	int butrect[4];
} barItem;

typedef struct {
	pmenuItem_t *items;
	int nitems;  
	barItem *bitems; //new
	int nbitems; //new
	//int nactive; //now refers to bitems
	GLubyte *lumalpha;
	GLuint textureID;
	GLfloat *vert;
	//GLfloat *tex;
	GLushort *ind;
	int blankItem;
	bool top; // true: menu appears at top of screen, else bottom
	float yoffset; // computed position of menu y
	int **radiosets;
	int *toggles;
} pmenu_t;



typedef struct {int x; int y;} XY;
typedef struct {
	GLfloat x;
	GLfloat y;
} FXY;
#include <list.h>

typedef struct pstatusbar{
	int loopcount;// = 0;
	int hadString;// = 0;
	int initDone;
	int showButtons;// =0;
	int statusbar_pinned;
	int menubar_pinned;
	int show_status;
	int show_menu;
	int yoff_status;
	//textureTableIndexStruct_s butts[mbuts][2];
	int butsLoaded;// = 0;
	int isOver;// = -1;
	int iconSize;// = 32;
	int buttonType;// = 1; /* 0 = rgba .png 1= .c bitmap (see above) */
	int savePng2dotc;// = 0; /* if you read png and want to save to a bitmap .c struct, put 1 */
	int showConText;// = 0;
	int showOptions;// =0;
	int showHelp;//=0;
	s_list_t *conlist;
	int concount;
	int fontInitialized;// = 0;
	//GLuint fwFontOffset[3];
	//XY fwFontSize[3];
	//int sb_hasString;// = FALSE;
	struct Uni_String *myline;
	//char buffer[200];
	char messagebar[200];
	int bmfontsize;// = 2; /* 0,1 or 2 */
	int optionsLoaded;// = 0;
	char * optionsVal[20];
	int osystem;// = 3; //mac 1btn = 0, mac nbutton = 1, linux game descent = 2, windows =3
	XY bmWH;// = {10,15}; /* simple bitmap font from redbook above, width and height in pixels */
	int bmScale; //1 or 2 for the hud pixel fonts, changes between ..ForOptions and ..Regular 
	int bmScaleForOptions; //special scale for the options check boxes (touch needs bigger)
	int bmScaleRegular; //scale non-clickable/non-touchable text ! ?
	int statusBarSize; //in pixels, should be bmScale x 16
	int posType; //1 == glRasterPos (opengl < 1.4), 0= glWindowPos (opengl 1.4+)
	pfont_t pfont;
   // Load the shaders and get a linked program object
   GLuint programObject; // = esLoadProgram ( vShaderStr, fShaderStr );
	GLuint positionLoc;
	GLuint texCoordLoc;
	GLuint textureLoc;
	GLuint color4fLoc;
	pmenu_t pmenu;
	int buttonSize; //size of menu buttons, in pixels - default 32
	GLfloat textColor[4];
	int screenWidth, screenHeight, clipPlane;
	int side_top, side_bottom;
}* ppstatusbar;
void *statusbar_constructor(){
	void *v = MALLOCV(sizeof(struct pstatusbar));
	memset(v,0,sizeof(struct pstatusbar));
	return v;
}
void statusbar_init(struct tstatusbar *t){
	//public
	//private
	t->prv = statusbar_constructor();
	{
		int i;
		ppstatusbar p = (ppstatusbar)t->prv;
		p->loopcount = 0;
		p->hadString = 0;

		p->showButtons =1;
		//p->statusbar_pinned = 1;
		//p->menubar_pinned = 0;
		p->butsLoaded = 0;
		p->isOver = -1;
		p->iconSize = 32;
		p->buttonType = 1; /* 0 = rgba .png 1= .c bitmap (see above) (put 0 to read .png, write C) */
		p->savePng2dotc = 0; /* if you read png and want to save to a bitmap .c struct, put 1 */
		p->showConText = 0;
		p->showOptions =0;
		p->showHelp = 0;
		p->fontInitialized = 0;
		//p->sb_hasString = FALSE;
		p->initDone = FALSE;
		p->optionsLoaded = 0;
		p->osystem = 3; //mac 1btn = 0, mac nbutton = 1, linux game descent = 2, windows =3
		p->bmWH.x = 8;
		p->bmWH.y = 15; //{10,15}; /* simple bitmap font from redbook above, width and height in pixels */
#ifdef TOUCH
		p->bmScaleForOptions = 2;
#else
		p->bmScaleForOptions = 1;
#endif
		p->bmScaleRegular = 1;
#ifdef KIOSK
		p->bmScaleRegular = 2;
		p->bmScaleForOptions = 2;
#endif
		p->bmScale = p->bmScaleRegular; //functions can change this on the fly
		p->statusBarSize = p->bmScaleRegular * 16;
		p->posType = 0; //assume ogl 1.4+, and correct if not
		p->pfont.cheight = 0;
		p->pfont.cwidth = 0;
		p->pfont.lumalpha = NULL;
		p->pmenu.items = MALLOC(pmenuItem_t *, 25 * sizeof(pmenuItem_t));
		for(i=0;i<25;i++) p->pmenu.items[i].butStatus = 0;
		p->pmenu.bitems = (barItem *)malloc(25 * sizeof(barItem));
		bzero(p->pmenu.bitems,25 * sizeof(barItem));

		//p->showOptions = p->butStatus[10] = 1; //for debugging hud text
		p->buttonSize = BUTSIZE;
		p->textColor[3] = 1.0f;
		p->screenWidth = 400;
		p->screenHeight = 200,
		p->clipPlane = p->statusBarSize;
	}
}

//ppstatusbar p = (ppstatusbar)gglobal()->statusbar.prv;

void initProgramObject(){
	ppstatusbar p = (ppstatusbar)gglobal()->statusbar.prv;

   // Load the shaders and get a linked program object
   p->programObject = esLoadProgram ( (const char*) vShaderStr, (const char *)fShaderStr );
   // Get the attribute locations
   p->positionLoc = glGetAttribLocation ( p->programObject, "a_position" );
   p->texCoordLoc = glGetAttribLocation ( p->programObject, "a_texCoord" );
   // Get the sampler location
   p->textureLoc = glGetUniformLocation ( p->programObject, "Texture0" );
   p->color4fLoc = glGetUniformLocation ( p->programObject, "Color4f" );
}
static int lenOptions   = 19;
void statusbar_clear(struct tstatusbar *t){
	//public
	//private
	{
		ppstatusbar p = (ppstatusbar)t->prv;
		int i;
	    glDeleteTextures(1, &(p->pfont.textureID));
		glDeleteTextures(1, &(p->pmenu.textureID));
		if(p->conlist)
			ml_delete_all(p->conlist);
		if(p->optionsVal)
		for(i=0;i<lenOptions;i++)
		{
			if(p->optionsVal[i])
				FREE_IF_NZ(p->optionsVal[i]);
		}
		if(p->pmenu.items)
			for(i=0;i<p->pmenu.nitems;i++)
				FREE_IF_NZ(p->pmenu.items[i].lumalpha);
		FREE_IF_NZ(p->pmenu.lumalpha);
		FREE_IF_NZ(p->pmenu.items);
		FREE_IF_NZ(p->pmenu.vert);
		FREE_IF_NZ(p->pmenu.ind);
		FREE_IF_NZ(p->pfont.lumalpha);
	}
}
void fwMakeRasterFonts()
{
	int i,j,k,m,w,h,bytewidth,bit;
    //int bit1;
    int ichar,isize, irow, icol, irowheight,icolwidth, iwidth, iheight;
	float width, height;
	GLubyte *cdata, *row;
	GLubyte white[2];
	//GLuint fwFontOffset8x15;
	ppstatusbar p = (ppstatusbar)gglobal()->statusbar.prv;

   //FW_GL_PIXELSTOREI(GL_UNPACK_ALIGNMENT, 1);
   //fwFontOffset8x15 = glGenLists (128);
	p->pfont.cheight = 15;
	p->pfont.cwidth = 8;
	// we'll make a squarish image, 16 characters wide, 16 char high
	// 2 bytes per pixel: luminance and alpha
	//height = p->pfont.cheight * 16;
	//width  = p->pfont.cwidth * 16;
	iheight = 16 * 16;
	iwidth = 16 * 16;
	height = (float)iheight;
	width = (float)iwidth;
	irowheight = 15;
	icolwidth = 8;
	isize = iheight * iwidth * 2; //(p->pfont.cheight *16) * (p->pfont.cwidth * 16) * 2;

	p->pfont.lumalpha = MALLOC(GLubyte *, isize);
	//memset(p->pfont.lumalpha,0,isize);
	memset(p->pfont.lumalpha,0,isize);
	white[0] = white[1] = (GLubyte)255;
	for(m=0;m<256;m++)
	{
		p->pfont.have[m] = 0;
	}
	for(m=0;m<128;m++)
	{
		ichar = fwLetters8x15[m][0];
		if(ichar == 255)break;
		p->pfont.have[ichar] = 1; //loaded
		cdata = &fwLetters8x15[m][7];
		w = fwLetters8x15[m][1];
		h = fwLetters8x15[m][2];
		//16 rows of 16 chars
		irow = ichar / 16;
		icol = ichar % 16;
		p->pfont.tex[0][0][ichar] = (GLfloat)(icol * icolwidth);
		p->pfont.tex[1][0][ichar] = (GLfloat)(irow * irowheight);
		p->pfont.tex[0][1][ichar] = p->pfont.tex[0][0][ichar] + p->pfont.cwidth;
		p->pfont.tex[1][1][ichar] = p->pfont.tex[1][0][ichar] + p->pfont.cheight;
		p->pfont.owh[0][0][ichar] = p->pfont.owh[1][0][ichar] = 0.0f;
		p->pfont.owh[0][1][ichar] = (GLfloat)p->pfont.cwidth; //8;
		p->pfont.owh[1][1][ichar] = (GLfloat)p->pfont.cheight; //.15;
		//normalize texture coords from image coords to 0-1 range
		for(j=0;j<2;j++) {
			p->pfont.tex[0][j][ichar] /= width;
			p->pfont.tex[1][j][ichar] /= height;
		}
		bytewidth = ((w-1)/8 +1);
		for(j=0;j<h;j++)
		{
			row = &cdata[j*bytewidth];
			for(i=0;i<w;i++)
			{
				k = i/8;
				//bit = row[k] & (1<<(w-i-1))? 1 : 0;
				bit = row[k] & (1<<((bytewidth*8)-i-1))? 1 : 0;
				if(bit)
				{
					//memcpy(&p->pfont.lumalpha[(((irow*15)+j)*8*16 + icol*8 + i)*2],white,2);
					int ip;
					ip = (irow*irowheight +j)*iwidth;
					ip += icol*icolwidth + i;
					memcpy(&p->pfont.lumalpha[ip*2],white,2);
					//memcpy(&p->pfont.lumalpha[(((irow*irowheight)+j)*icolwidth*16 + icol*icolwidth + i)*2],white,2);
				}
			}
		}
	}
	if(false){
		//int k;
		FILE * fp;
		fp = fopen("hud_junk_0.txt","w+");
		fprintf(fp,"char data\n");
		for(m=0;m<128;m++)
		{
			ichar = fwLetters8x15[m][0];
			if(ichar == 255)break;
			fprintf(fp,"%c %d ",(char)ichar,ichar);
			fprintf(fp,"tex %6.2f %6.2f %6.2f %6.2f",p->pfont.tex[0][0][ichar],p->pfont.tex[1][0][ichar],p->pfont.tex[0][1][ichar],p->pfont.tex[1][1][ichar]);
			fprintf(fp,"ohw %6.2f %6.2f %6.2f %6.2f",p->pfont.owh[0][0][ichar],p->pfont.owh[1][0][ichar],p->pfont.owh[0][1][ichar],p->pfont.owh[1][1][ichar]);
			fprintf(fp,"\n");
		}
		fclose(fp);
	}

    glGenTextures(1, &(p->pfont.textureID));
	//p->pfont.textureID = LoadTexture ( "basemap.tga" );
    glBindTexture(GL_TEXTURE_2D, p->pfont.textureID);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST); //GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST); //GL_LINEAR);

    glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE_ALPHA, iwidth, iheight, 0, GL_LUMINANCE_ALPHA , GL_UNSIGNED_BYTE, p->pfont.lumalpha);
    //glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE_ALPHA, 16*16, irowheight*16, 0, GL_LUMINANCE_ALPHA , GL_UNSIGNED_BYTE, p->pfont.lumalpha);
    //glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 32, 32, 0, GL_RGBA , GL_UNSIGNED_BYTE, cursor);

}

void initFont(void)
{
	/*initialize raster bitmap font above */
  // FW_GL_SHADEMODEL (GL_FLAT);
	ppstatusbar p = (ppstatusbar)gglobal()->statusbar.prv;

   fwMakeRasterFonts();
   p->fontInitialized = 1;
}
//int bmfontsize = 2; /* 0,1 or 2 */
//static int ibufvert, ibuftex, ibufidx;
void printString(char *s){}
FXY screen2normalizedScreen( GLfloat x, GLfloat y);
FXY screen2normalizedScreenScale( GLfloat x, GLfloat y);
#ifndef DISABLER
#include <malloc.h>
#else
#include <malloc/malloc.h>
#endif
void printString2(GLfloat sx, GLfloat sy, char *s)
{
	int i, j;
    //int k,kk;
    int len, ichar;
	FXY charScreenSize;
	ppstatusbar p = (ppstatusbar)gglobal()->statusbar.prv;
	GLfloat x,y,z;
    GLfloat *vert;
    GLfloat *tex;
    GLushort* ind;
	int sizeoftex, sizeofvert, sizeofind;

	// construct triangle list
	if(!s) return;
	len = (int) strlen(s);
	if(len == 0) return;
	sizeofvert = len * sizeof(GLfloat) * 4 * 3;
	sizeoftex = len * sizeof(GLfloat) * 4 * 2;
	sizeofind = len * sizeof(GLshort) * 2 * 3;
	vert = (GLfloat*)alloca(sizeofvert); //2 new vertex, 3D
	tex  = (GLfloat*)alloca(sizeoftex); //4 new texture coords, 2D
	ind  = (GLshort*)alloca(sizeofind); //2 triangles, 3 points each
	x=y=z = 0.0f;
	x = sx;
	y = sy;
	i = 0;
	// 1 2     coords and tex coords pattern
	// 0 3
	for(j=0;j<len;j++)
	{
		ichar = (int)s[i];
		if (ichar == '\t') ichar = ' '; //trouble with tabs, quick hack
		if(p->pfont.have[ichar])
		{
			charScreenSize = screen2normalizedScreenScale(p->pfont.owh[0][1][ichar]*p->bmScale,p->pfont.owh[1][1][ichar]*p->bmScale);
			vert[i*4*3 +0] = x;
			vert[i*4*3 +1] = y;
			vert[i*4*3 +2] = z;
			vert[i*4*3 +3] = x;
			vert[i*4*3 +4] = y + charScreenSize.y;
			vert[i*4*3 +5] = z;
			vert[i*4*3 +6] = x + charScreenSize.x; 
			vert[i*4*3 +7] = y + charScreenSize.y; 
			vert[i*4*3 +8] = z;
			vert[i*4*3 +9] = x + charScreenSize.x; 
			vert[i*4*3+10] = y;
			vert[i*4*3+11] = z;
			x = x + charScreenSize.x; 
			tex[i*4*2 +0] = p->pfont.tex[0][0][ichar];
			tex[i*4*2 +1] = p->pfont.tex[1][0][ichar];
			tex[i*4*2 +2] = p->pfont.tex[0][0][ichar];
			tex[i*4*2 +3] = p->pfont.tex[1][1][ichar];
			tex[i*4*2 +4] = p->pfont.tex[0][1][ichar];
			tex[i*4*2 +5] = p->pfont.tex[1][1][ichar];
			tex[i*4*2 +6] = p->pfont.tex[0][1][ichar];
			tex[i*4*2 +7] = p->pfont.tex[1][0][ichar];
			ind[i*3*2 +0] = i*4 + 0;
			ind[i*3*2 +1] = i*4 + 1;
			ind[i*3*2 +2] = i*4 + 2;
			ind[i*3*2 +3] = i*4 + 2;
			ind[i*3*2 +4] = i*4 + 3;
			ind[i*3*2 +5] = i*4 + 0;
			i++;
		}
	}
	//bindTexture and DrawElements calls are the same for GL and GLES2

	glActiveTexture ( GL_TEXTURE0 );
	glBindTexture ( GL_TEXTURE_2D, p->pfont.textureID );
	// Load the vertex position
	glVertexAttribPointer ( p->positionLoc, 3, GL_FLOAT, 
						   GL_FALSE, 0, vert );
	// Load the texture coordinate
	glVertexAttribPointer ( p->texCoordLoc, 2, GL_FLOAT,
						   GL_FALSE, 0, tex );  //fails - p->texCoordLoc is 429xxxxx - garbage

	glEnableVertexAttribArray ( p->positionLoc );
	glEnableVertexAttribArray ( p->texCoordLoc );
	// Set the base map sampler to texture unit to 0
	glUniform1i ( p->textureLoc, 0 );
	glDrawElements ( GL_TRIANGLES, i*3*2, GL_UNSIGNED_SHORT, ind );
	//FREE(vert);
	//FREE(tex);
	//FREE(ind);


}

void render_init(void);

///* make sure that on a re-load that we re-init */
//void kill_status (void) {
//	/* hopefully, by this time, rendering has been stopped */
//	ppstatusbar p = (ppstatusbar)gglobal()->statusbar.prv;
//
//	p->sb_hasString = FALSE;
//	p->buffer[0] = '\0';
//}
//
//
///* trigger a update */
//void update_status(char* msg) {
//	ppstatusbar p = (ppstatusbar)gglobal()->statusbar.prv;
//
//	if (msg==NULL){
//		p->sb_hasString = FALSE;
//		p->buffer[0] = '\0';
//	}else {
//		p->sb_hasString = TRUE;
//		strcpy (p->buffer,msg);
//	}
//}
//char *get_status(){
//	ppstatusbar p = (ppstatusbar)gglobal()->statusbar.prv;
//	return p->buffer;
//}

/* start cheapskate widgets >>>> */
//static int lenOptions   = 16;
char * optionsText[] = {
"stereovision:",
"  side-by-side",
"  up-down",
"  anaglyph",
"  shutter",
"Eyebase - object space",
"\36       \37",
"Your Eyebase = fiducials",
"\36       \37",
"Anaglyph",
" RGB",
"     left",
"     right",
"     neither",
"  pin statusbar",
"  pin menubar",
"colorScheme:",
"",
"target FPS \36    \37",
NULL,
};
//int optionsLoaded = 0;
//char * optionsVal[15];
void setOptionsVal()
{
}
char *colorschemenames [] = {
"original",
"midnight",
"angry",
"favicon",
"aqua",
"neon:lime",
"neon:yellow",
"neon:cyan",
"neon:pink",
NULL,
};

void initOptionsVal()
{
	int i,j,k;
	X3D_Viewer *viewer;
	ppstatusbar p = (ppstatusbar)gglobal()->statusbar.prv;
	viewer = Viewer();

	for(i=0;i<lenOptions;i++)
	{
		if(!p->optionsVal[i])
			p->optionsVal[i] = MALLOC(char*, 20);
		for(j=0;j<9;j++) p->optionsVal[i][j] = ' ';
		p->optionsVal[i][8] = '\0';
	}
	p->optionsVal[1][0] = 034; //[]
	p->optionsVal[2][0] = 034; //[]
	p->optionsVal[3][0] = 034; //[]
	p->optionsVal[4][0] = 034; //[]

	if(viewer->sidebyside)
		p->optionsVal[1][0] = 035; //[*] '*';
	if(viewer->updown)
		p->optionsVal[2][0] = 035; //[*] '*';
	if(viewer->anaglyph)
		p->optionsVal[3][0] = 035; //[*] '*';
	if(viewer->shutterGlasses)
		p->optionsVal[4][0] = 035; //[*] '*';
	sprintf(p->optionsVal[6],"  %4.3f",viewer->eyedist); //.eyebase); //.060f);
	sprintf(p->optionsVal[8],"  %4.3f",viewer->screendist); //.6f);
	//sprintf(p->optionsVal[7],"  %4.3f",viewer->stereoParameter); //.toein.4f);
	for(i=0;i<3;i++){
		for(j=0;j<3;j++){
			k = getAnaglyphPrimarySide(j,i);
			p->optionsVal[11+i][j+1] = (k ? 035 : ' ');
		}
	}
	fwl_get_sbh_pin(&p->statusbar_pinned,&p->menubar_pinned);
	p->optionsVal[14][0] = p->statusbar_pinned ? 035 : 034; 
	p->optionsVal[15][0] = p->menubar_pinned ? 035 : 034; 
	sprintf(p->optionsVal[17]," %s ",fwl_get_ui_colorschemename());
	sprintf(p->optionsVal[18],"            %4d",fwl_get_target_fps());
	p->optionsLoaded = 1;
}
void updateOptionsVal()
{
	/* on each loop we refresh the hud Options state from the viewer state, 
	in case others - via keyboard or gui - are also updating the viewer state*/
	initOptionsVal();
}
/* the optionsCase char is used in a switch case later to involk the appropriate function */
char * optionsCase[] = {
"             ",
"22222222222222",
"44444444",
"33333333",
"11111111",
"       ",
"55     66",
"              ",
"DDEEEEEFF",
"        ",
"       ",
" rst     ",
" uvw      ",
" xyz      ",
"77777777",
"88888888",
"        ",
"99999999",
"          KK    LL",
NULL,
};

XY mouse2screen(int x, int y)
{
	XY xy;
	xy.x = x;
	xy.y = ((ppstatusbar)(gglobal()->statusbar.prv))->screenHeight -y;
	return xy;
}
XY screen2text(int x, int y)
{
	XY rc;
	int topOffset;
	ppstatusbar p; 
	ttglobal tg = gglobal();
	p = (ppstatusbar)tg->statusbar.prv;

	topOffset = p->side_top;
	if(p->pmenu.top) topOffset += p->buttonSize;
	rc.x = x/(p->bmWH.x*p->bmScale) -1; //10; 
	rc.y = (int)((p->screenHeight -y - topOffset)/(p->bmWH.y*p->bmScale)); //15.0 ); 
	rc.y -= 1;
	return rc;
}
XY text2screen( int col, int row)
{
	XY xy;
	int topOffset;
	ppstatusbar p; 
	ttglobal tg = gglobal();
	p = (ppstatusbar)tg->statusbar.prv;
	topOffset = p->side_top;
	if(p->pmenu.top) topOffset += p->buttonSize;
	xy.x = (col+1)*p->bmWH.x*p->bmScale; //10; 
	xy.y = p->screenHeight - topOffset - (row+2)*p->bmWH.y*p->bmScale; //15;
	return xy;
}
FXY screen2normalizedScreenScale( GLfloat x, GLfloat y)
{
	FXY xy;
	ppstatusbar p = (ppstatusbar)gglobal()->statusbar.prv;

	//convert to -1 to 1 range
	xy.x = ((GLfloat)x/(GLfloat)p->screenWidth * 2.0f);
	xy.y = ((GLfloat)y/(GLfloat)p->screenHeight * 2.0f);
	return xy;
}
FXY screen2normalizedScreen( GLfloat x, GLfloat y)
{
	FXY xy;
	//convert to -1 to 1 range
	xy = screen2normalizedScreenScale(x,y);
	xy.x -= 1.0;
	xy.y -= 1.0;
	return xy;
}
void printOptions()
{
	int j; 
	ppstatusbar p = (ppstatusbar)gglobal()->statusbar.prv;

	//printTextCursor();
	if(!p->optionsLoaded) initOptionsVal();
	updateOptionsVal(); //ideally we would be stateless in the hud, let Viewer hold state, that way other gui/shortcuts can be used

	p->bmScale = p->bmScaleForOptions;
	for(j=0;j<lenOptions;j++)
	{
		FXY xy;
		XY xy0 = text2screen(0,j);
		xy = screen2normalizedScreen( (GLfloat)xy0.x, (GLfloat)xy0.y);
		printString2(xy.x,xy.y,p->optionsVal[j]);  /* "  0.050  " */
		printString2(xy.x,xy.y,optionsText[j]); /* "<       >" */
	}
	p->bmScale = p->bmScaleRegular;

}

int handleOptionPress(int mouseX, int mouseY)
{
	/* general idea: we don't update the hud/option state here - just the Viewer state - then 
	  refresh the hud/options state from the Viewer on each statusbar draw iteration
	*/
	int opt;
	XY xys;
	XY xyt;
	X3D_Viewer *viewer;
	ppstatusbar p;
	ttglobal tg = gglobal();
	p = (ppstatusbar)tg->statusbar.prv;

	viewer = Viewer();

	p->bmScale = p->bmScaleForOptions;
	xys = mouse2screen(mouseX,mouseY);
	p->side_top = 0;
	if (Viewer()->updown) p->side_top = p->screenHeight / 2;
	xyt = screen2text(xys.x,xys.y);
	opt = ' ';
	if( 0 <= xyt.y && xyt.y < lenOptions )
	{
		int len = (int) strlen(optionsCase[xyt.y]);
		if( xyt.x < len )
		{
			/* we are on an options line */
			opt = optionsCase[xyt.y][xyt.x];
		}
	}
	if(opt == ' ') return 0;
	p->bmScale = p->bmScaleRegular;

	/* we're clicking a sensitive area.  */
	switch(opt) 
	{
	case 'T': {
		/* EAI */
		/* Note, this is actually useless (I suspect) because the EAI would already have started / (or ignored) */
		printf("toggle EAI");
		/* fwl_setp_eai(1 - fwl_getp_eai()); */
		break;}
	case '1': 
	case '2': 
	case '3': 
	case '4': 
		toggleOrSetStereo(opt-'0');
		break;
	case '7': 
		fwl_get_sbh_pin(&p->statusbar_pinned,&p->menubar_pinned);
		p->statusbar_pinned = 1 - p->statusbar_pinned;
		fwl_set_sbh_pin(p->statusbar_pinned,p->menubar_pinned);
		break;
	case '8': 
		fwl_get_sbh_pin(&p->statusbar_pinned,&p->menubar_pinned);
		p->menubar_pinned = 1 - p->menubar_pinned;
		fwl_set_sbh_pin(p->statusbar_pinned,p->menubar_pinned);
		break;
	case '9':
		fwl_next_ui_colorscheme();
		break;
	case 'r': 
	case 's': 
	case 't': 
		setAnaglyphPrimarySide(opt-'r',0); //L,R,N
		break;
	case 'u': 
	case 'v': 
	case 'w': 
		setAnaglyphPrimarySide(opt-'u',1); //L,R,N
		break;
	case 'x': 
	case 'y': 
	case 'z': 
		setAnaglyphPrimarySide(opt-'x',2); //L,R,N
		//setAnaglyphSideColor(opt,1);
		break;
	case '5': {
		/* eyebase */
		printf("reduce eyebase");
		viewer->eyedist *= .9;
		updateEyehalf();
		break;}
	case '6': {
		/* eyebase */
		printf("increase eyebase");
		viewer->eyedist *= 1.1;
		updateEyehalf();
		break;}
	case 'D': {
		/* screendist */
		printf("reduce screendist");
		viewer->screendist -= .02; //*= .9;
		updateEyehalf();
		break;}
	case 'E': {
		/* screendist */
		printf("set screendist");
		break;}
	case 'F': {
		/* screendist */
		printf("increase screendist");
		viewer->screendist += .02; //*= 1.1;
		if(viewer->sidebyside)
			viewer->screendist = min(viewer->screendist,.375);
		updateEyehalf();
		break;}
	case 'H': {
		/* toein */
		printf("reduce toe-in");
		viewer->stereoParameter *= .9;
		updateEyehalf();
		break;}
	case 'I': {
		/* toein */
		printf("set toe-in");
		break;}
	case 'J': {
		/* toein */
		printf("increase toe-in");
		viewer->stereoParameter *= 1.1;
		if(viewer->sidebyside)
			viewer->stereoParameter = min(viewer->stereoParameter,.01);  //toe-in is dangerous in sidebyside because it can force you to go wall-eyed
		updateEyehalf();
		break;}
	case 'K':
	case 'L':
		{
			//for target frames_per_second choices, we'd like a nice pow2 series like 7, 15, 30, 60, 120, 240 FPS
			int i15, tfps;
			tfps = fwl_get_target_fps();
			i15 = (int)((double)tfps / 15.0 + .5);
			if(opt == 'K') i15 /= 2;
			if(opt == 'L') i15 = max(1,i15*2);
			if(i15 < 1) tfps = 7;
			else tfps = min(3840,(int)15*i15);
			fwl_set_target_fps(tfps);
		}
	default: {break;}
	}
	return 1;
}
/* <<< end cheapskate widgets */



//int osystem = 3; //mac 1btn = 0, mac nbutton = 1, linux game descent = 2, windows =3
#if defined(QNX) //|| defined(_MSC_VER)
int lenhelp = 21;
char * keyboardShortcutHelp[] = {
"WALK Mode",
"   movement: drag left/right for turns;",
"             drag up/down for forward/backward", 
"FLY Mode",
"   use the buttons for these motions:",
"   bird: drag left/right for left/right turns",
"		  drag up/down for foreward/backward",
"   tilt up/down",
"   translation up/down and left/right",
"   rotation about the viewpoint/camera axis",
"EXAMINE Mode",
"   rotation: drag left/right or up/down",
"Level to bound viewpoint",
"Flashlight/headlight",
"Collision (and for WALK also gravity)",
"Previous, Next viewpoint",
"(this Help)",
"Console messages from the program",
"Options",
"Reload last scene",
"Enter URL of .x3d or .wrl scene"
#elif defined(KIOSK) //|| defined(_MSC_VER)
int lenhelp = 19;
char * keyboardShortcutHelp[] = {
"WALK Mode",
"   movement: drag left/right for turns;",
"             drag up/down for forward/backward", 
"FLY Mode",
"   use the buttons for these motions:",
"   bird: drag left/right for left/right turns",
"		  drag up/down for foreward/backward",
"   tilt up/down",
"   translation up/down and left/right",
"   rotation about the viewpoint/camera axis",
"EXAMINE Mode",
"   rotation: drag left/right or up/down",
"Level to bound viewpoint",
"Flashlight/headlight",
"Collision (and for WALK also gravity)",
"Previous, Next viewpoint",
"(this Help)",
"Console messages from the program",
"Options"
#elif defined(_MSC_VER_NOT)
int lenhelp = 16;
char * keyboardShortcutHelp[] = {
"WALK Mode",
"   movement: drag left/right for turns;",
"             drag up/down for forward/backward", 
"Keyboard FLY Mode",
"   use the keyboard for these motions:",
"   8 k rotation down/up",
"   u o rotation left/right",
"   7 9 rotation about the Z axis",
"   a z translation forwards/backwards",
"   j l translation left/right",
"   p ; translation up/down",
" or use arrow keys. to change keychord: press SHIFT->",
"EXAMINE Mode",
"   rotation: drag left/right or up/down",
"EXPLORE Mode - use CTRL-click to recenter",
"hit spacebar to get console prompt :, then type help"
#else
int lenhelp = 20;
char * keyboardShortcutHelp[] = {
"EXAMINE Mode",
"   LMB rotation: MX rotation around Y axis; MY rotation around X axis",
"   RMB zooms", // On Apple computers with one button mice, press and hold the "control" key, and use your mouse. 
"WALK Mode",
"   LMB movement: MX left/right turns; MY walk forward/backward", 
"   RMB height", //se Button 3 moves you up/down (changes your height above the ground). On Apple computers with one button mice, press and hold the "control" key, and use your mouse. 
"EXPLORE Mode",
" - use CTRL-click to recenter",
"Keyboard navigation",
" - use arrow keys. to change keychord: press SHIFT> or SHIFT<",
"other",
"  e Switch to Examine navigation mode",
"  w Switch to Walk navigation mode",
"  v Go to next viewpoint in the scene",
"  b Go to previous viewpoint in the scene",
"  / Print current viewport local pose", 
"  h Toggle headlight",
"  c Toggle collision detection",
"  x Snapshot",
"  q Quit browser",
#endif
NULL,
};
const char *libFreeWRL_get_version();
void printKeyboardHelp(ppstatusbar p)
{
	int j; 
	XY xy;
	FXY fxy;
	static const char *versionInfo = "libfreeWRL version ";
	xy = text2screen(0,0);
	fxy = screen2normalizedScreen((GLfloat)xy.x,(GLfloat)xy.y);
	printString2(fxy.x,fxy.y,(char *)versionInfo);
	xy = text2screen((int)strlen(versionInfo),0);
	fxy = screen2normalizedScreen((GLfloat)xy.x,(GLfloat)xy.y);
	printString2(fxy.x,fxy.y,(char*)libFreeWRL_get_version());

	for(j=0;j<lenhelp;j++)
	{
		xy = text2screen(0,j+1);
		fxy = screen2normalizedScreen((GLfloat)xy.x,(GLfloat)xy.y);
		printString2(fxy.x,fxy.y,keyboardShortcutHelp[j]);
	}
}

void hudSetConsoleMessage(char *buffer)
{
	s_list_t* last;
	/*calling program passes ownership of buffer here, where we free when scrolling off */
	//char *ln;
	//int linelen;
	ppstatusbar p = (ppstatusbar)gglobal()->statusbar.prv;

	//printf("+%s\n", buffer);
	if(!p->conlist)
	{
		//char * line;
		//line = MALLOC(char *, 2);
		//line[0] = '\0';
		//p->conlist = ml_new(line);
		//p->concount = 1;
		p->concount = 0;
	}
	last = ml_new(buffer);
	if (!p->conlist)
		p->conlist = last;
	else
		ml_append(p->conlist,last);
	p->concount++;
	if( p->concount > 50 ) // > MAXMESSAGES number of scrolling lines
	{
		//printf("-%s\n", (char*)p->conlist->elem);
		free((char*)p->conlist->elem); //free a previous buffer now scrolled up off the screen
		p->conlist = ml_delete_self(p->conlist, p->conlist); /*delete from top*/
		p->concount--;
	}
}

void printConsoleText()
{
	/* ConsoleMessage() comes out as a multi-line history rendered over the scene */
	int jstart;
	int j = 0;
	XY xybottom;
	ppstatusbar p = (ppstatusbar)gglobal()->statusbar.prv;

	jstart = j;
	{
		s_list_t *__l;
		s_list_t *next;
		s_list_t *_list = p->conlist;
		/* lets keep the scrolling text from touching the bottom of the screen */
		xybottom = screen2text(0,p->side_bottom); 
		jstart = max(0,p->concount-(xybottom.y - 3)); /* keep it 3 lines off the bottom */
		for(__l=_list;__l!=NULL;) 
		{
			next = ml_next(__l); /* we need to get next from __l before action deletes element */ 
			if(j >= jstart) /* no need to print off-screen text */
			{
				FXY fxy;
				XY xy = text2screen(0,j-jstart);
				fxy = screen2normalizedScreen((GLfloat)xy.x,(GLfloat)xy.y);
				printString2(fxy.x,fxy.y,__l->elem); 
			}
			j++;
			__l = next; 
		}
	}
}

enum {
ACTION_WALK,
ACTION_FLY2,
ACTION_TILT,
ACTION_TPLANE,
ACTION_RPLANE,
ACTION_FLY,
ACTION_EXAMINE,
ACTION_EXPLORE,
ACTION_SPHERICAL,
ACTION_TURNTABLE,
ACTION_LOOKAT,
ACTION_YAWZ,
ACTION_YAWPITCH,
ACTION_ROLL,
ACTION_XY,
ACTION_DIST,
ACTION_SHIFT,
ACTION_LEVEL,
ACTION_HEADLIGHT,
ACTION_COLLISION,
ACTION_PREV,
ACTION_NEXT,
ACTION_HELP,
ACTION_MESSAGES,
ACTION_OPTIONS,
ACTION_RELOAD,
ACTION_URL,
ACTION_FILE,
ACTION_BLANK
} button_actions;
void convertPng2hexAlpha()
{
	/* How to make new button icons:
		1. design a button gray or white over alpha in something like blender
		2. render to 32x32 .png with alpha channel
		3. p->buttonType = 0 in function that calls convertPng2hexAlpha
		4. change mbuts to 1 if doing just one button
		5. put the name of button for butFnames[] = {"mybutname.png"};
		6. put your mybutname.png in the folder where freewrl runs from (see diagnostic GetCurrentDirectory below)
		7. build and run freewrl once - should get hudicons_octalpha_h output file
		8. copy and paste from hudicons_octalpha_h to freewrl's hudicons_octalpha.h at the bottom (leave existing buttons)
		9. // p->buttonType = 0 - comment back out in function below
		10. tinker with code in statusbarhud.c in several places to get the button to show and do things
	*/
	int w,h,ii,size;
	static int mbuts = 2; //8; // 17;
	static char * butFnames[] = {"shift.png","sensor.png"}; //{"YAWZ.png"}; // {"lookat.png","explore.png","spherical.png","turntable.png","XY.png","ROLL.png","YAWPITCH.png","YAWZ.png"}; //{"tilt.png"}; //{"tplane.png","rplane.png","walk.png","fly.png","examine.png","level.png","headlight.png","collision.png","prev.png","next.png","help.png","messages.png","options.png","reload.png","url.png","file.png","blank.png"};//"flyEx.png",
	textureTableIndexStruct_s butts;

	FILE* out = fopen("hudIcons_octalpha_h","w+");
	//{
	//	//where to put .png, windows desktop
	//	char dirname[1024];
	//	GetCurrentDirectory(1000,dirname); //not supported in winRT
	//	printf("current directory:%s\n",dirname);
	//}
	/* png icon files (can have transparency) problem: you need to put them in the current working directory*/
	for(ii=0;ii<mbuts;ii++)
	{
		int j,k,l,g,rgbmax[3];
		texture_load_from_file(&butts, butFnames[ii]);
		/* compute grayed out (non active) versions */
		w = butts.x;
		h = butts.y;
		size = w * h * 4;
		//step 1 find maximum RGB
		for(j=0;j<3;j++) rgbmax[j] = 0;
		for(j=0;j<butts.x;j++)
		{
			for(k=0;k<butts.y;k++)
			{
				for(l=0;l<3;l++)
				{
					g = butts.texdata[j*w*4 + k*4 + l];
					rgbmax[l] = g > rgbmax[l] ? g : rgbmax[l];
				}
			}
		}
		//step 2 scale to max color (for maximum white 255,255,255 in solid areas)
		for(j=0;j<butts.x;j++)
		{
			for(k=0;k<butts.y;k++)
			{
				g = 0;
				//scale color to maximum color (so solid will be 255,255,255 white
				for(l=0;l<3;l++)
				{
					int h;
					h = butts.texdata[j*w*4 + k*4 + l];
					h = (int)((float)h/(float)rgbmax[l]*255.0f);
					g += h;
				}
				//convert to gray so I can take any channel later
				g = g / 3; //convert colorful RGB to gray RGB
				g = g > 255? 255 : g;
				for(l=0;l<3;l++)
					butts.texdata[j*w*4 + k*4 + l] = g; 
			}
		}
		/* write rgba out as binary bitmap in .c struct format for inclusion above */
		{
			//unsigned char row, a, bit;
			char butname[30];
			strcpy(butname,butFnames[ii]);
			for(j=0;j<(int)strlen(butname);j++)
				if(butname[j] == '.') {butname[j] = '\0'; break;}
			fprintf(out,"GLubyte %s[] = {\n",butname); 
			//2012 1 byte per pixel method (nicer)
			{
				//this method writes only the alpha channel, and does it in octal strings
				//(to reconstruct luminance later, copy alpha to lum)
				//this makes a nice compact header file.
				char str[5];
				unsigned char *data;
				int i,m,n,lastlen;
				bool lastoct;

				fprintf(out,"\"");
				n = 0;
				lastoct = false;
				lastlen = 0;
				data = &butts.texdata[0]; //start on the alpha [3]
				for(i=0;i<size;i+=4) //do every 4th (skip RGB, just alpha)
				{
					int datai;
					// value = Red * Alpha (if I don't do this, the reconstructed lumalpha icons will look fat/ swollen/ smudged
					datai = (int)(((float) data[i] * (float)data[i+3])/255.0f);
					//this octal string writing method matches how Gimp write images to C 
					//weird part: if an octal /xxx has less than 3 digits, and the next
					//thing is a '0' to '9' then you need to break the string with 
					//an extra "" to interupt the octal string representation
					if( datai == '"' || datai == '\\') {sprintf(str,"\\%c",datai); lastoct = false;}
					else if( datai >= '0' && datai <= '9' && lastoct && lastlen < 4) {sprintf(str,"\"\"%c",datai); lastoct = false;}
					else if( datai > 32 && datai < 127 ) {sprintf(str,"%c",datai); lastoct = false;}
					else {sprintf(str,"\\%o",datai); lastoct = true;}
					fprintf(out,"%s",str);
					m = (int) strlen(str);
					n += m;
					lastlen = m;
					if(n > 71)
					{
						fprintf(out,"\"\n\"");
						n = 0;
					}
				}
				fprintf(out,"\"\n");

			}
			fprintf(out,"};\n");
		}
	} //i=1,mbut
	fclose(out);
	exit(0); //close the program, 
	//rename hudIcons_octalpha_h to hudIcons_octalpha.h and rebuild
	//then rerun with p->buttonType = 1
}

void initButtons()
{
	/* first time renderButtons() is called, this is called to 
		load the button icons and set up coords in pixels*/
	int i, buttonAtlasSizeCol, buttonAtlasSizeRow, buttonAtlasSquared;
	ttglobal tg = gglobal();
	ppstatusbar p = (ppstatusbar)tg->statusbar.prv;
	p->clipPlane = p->statusBarSize; //16;
	
	///p->buttonType = 0; //uncomment this line to convert png buttons to hudIcons_octalpha_h header format
	if(p->buttonType == 0){
		convertPng2hexAlpha();
		exit(0);
	}
	if(p->buttonType == 1)
	{

		//buttonlist, actionlist and NACTION are/mustbe synchronized, will become part of pmenitem tuple together
		// - include all buttons and actions here (filter out ones you don't want in mainbar)
		static GLubyte * buttonlist [] = {
			walk, fly, examine,
			yawz, xy, yawpitch, roll,
			explore, spherical, turntable, lookat, distance, 
			shift, level, headlight,
			collision, prev, next, help, messages, options, reload, url, file, blank
			};
		static int actionlist [] = {
			ACTION_WALK, ACTION_FLY, ACTION_EXAMINE,
			ACTION_YAWZ, ACTION_XY, ACTION_YAWPITCH, ACTION_ROLL,
			ACTION_EXPLORE, ACTION_SPHERICAL, ACTION_TURNTABLE, ACTION_LOOKAT, ACTION_DIST, 
			ACTION_SHIFT, ACTION_LEVEL, ACTION_HEADLIGHT, ACTION_COLLISION, ACTION_PREV,
			ACTION_NEXT, ACTION_HELP, ACTION_MESSAGES, ACTION_OPTIONS,
			ACTION_RELOAD, ACTION_URL, ACTION_FILE, ACTION_BLANK,
			};
		static int NACTION = 25; //must match buttonlist and actionlist count
		//radiosets are to indicate what things are deselected (if any) when another thing is selected
		static int radiosets [][9] = {
			{8,ACTION_FLY,ACTION_WALK,ACTION_EXAMINE,ACTION_EXPLORE,ACTION_SPHERICAL,ACTION_TURNTABLE,ACTION_LOOKAT,ACTION_DIST},
			{3,ACTION_MESSAGES,ACTION_OPTIONS,ACTION_HELP}, 
			//{4,ACTION_YAWZ, ACTION_XY, ACTION_YAWPITCH, ACTION_ROLL}, 
			{0},
			};
		//not sure we need to toggle in the View, the Model holds the state, and
		//controller checks once per frame
		static int toggles [] = {
			ACTION_COLLISION,ACTION_HEADLIGHT,ACTION_SHIFT,
			ACTION_HELP,ACTION_MESSAGES,ACTION_OPTIONS,0
			}; 
		static int togglesets [][8] = {{ACTION_FLY,4,ACTION_YAWZ, ACTION_XY, ACTION_YAWPITCH, ACTION_ROLL},{0}};
		//main menubar initial layout new mar 2015
		static int mainbar_withFileOpen [] = {
			ACTION_WALK, ACTION_FLY, ACTION_EXAMINE,
			ACTION_EXPLORE, ACTION_SPHERICAL, ACTION_TURNTABLE, ACTION_LOOKAT, ACTION_DIST, 
			ACTION_SHIFT, ACTION_LEVEL, ACTION_HEADLIGHT, ACTION_COLLISION, ACTION_PREV,
			ACTION_NEXT, ACTION_HELP, ACTION_MESSAGES, ACTION_OPTIONS, 
			//ACTION_RELOAD, ACTION_URL, 
			ACTION_FILE,
			-1,
			};
		static int mainbar_linux [] = {
			ACTION_WALK, ACTION_FLY, ACTION_EXAMINE,
			ACTION_EXPLORE, ACTION_SPHERICAL, ACTION_TURNTABLE, ACTION_LOOKAT, ACTION_DIST,
			ACTION_SHIFT, ACTION_LEVEL, ACTION_HEADLIGHT, ACTION_COLLISION, ACTION_PREV,
			ACTION_NEXT, ACTION_HELP, ACTION_MESSAGES, ACTION_OPTIONS, 
			//ACTION_RELOAD, ACTION_URL, 
			//ACTION_FILE,
			-1,
			};
		static int *mainbar = NULL;

		p->pmenu.nitems = NACTION; //number of action items, even if not shown on menubar
		mainbar = mainbar_linux;
//#ifdef _MSC_VER
//		mainbar = mainbar_withFileOpen;
//#endif
		//count number of menubar items, assuming last item is -1 sentinal value
		i=0;
		do{
			i++;
			p->pmenu.nbitems = i;
		}while(mainbar[i]>-1);
		//p->pmenu.nbitems = 18;
#if defined(QNX) || defined(KIOSK)
		p->pmenu.top = true;
#else
		p->pmenu.top = false;
#endif


		//convert to lumalpha
		//p->pmenu.items = (pmenuItem_t *)malloc(16 * sizeof(pmenuItem_t)); done in module init
		//may 1, 2012: QNX GLES2 needs power-of-2 image dimensions, but doesn't need to be square
		//bad: 32x5 by 32x5 (25 icons)  good: 32x8 by 32x4 (8x4 = 32 icons)
		buttonAtlasSizeCol = 8; // 8x4 grid of buttons
		buttonAtlasSizeRow = 4;
		buttonAtlasSquared = buttonAtlasSizeCol*buttonAtlasSizeRow;
		p->pmenu.lumalpha = MALLOC(GLubyte*, 32*32*2 *buttonAtlasSquared); //4x4 grid of icons each 32x32x2
		memset(p->pmenu.lumalpha,0,32*32*2 *buttonAtlasSquared);
		p->pmenu.vert= MALLOC(GLfloat*, 3*4*buttonAtlasSquared*sizeof(GLfloat));
//		p->pmenu.tex = MALLOC(GLfloat*, 2*4*buttonAtlasSquared*sizeof(GLfloat));
		p->pmenu.ind = MALLOC(GLushort*, 3*2*buttonAtlasSquared*sizeof(GLushort));
		p->pmenu.yoffset = 0.0f;
		if(p->pmenu.top) p->pmenu.yoffset = (float)(p->screenHeight - p->buttonSize); //32.0f;
		for(i=0;i<p->pmenu.nitems;i++)
		{
			int j,k,irow,icol;
			int mv,mt,mi,kv,kt;
			GLfloat dx;
			FXY xyxy[2];
			p->pmenu.items[i].action = actionlist[i];
			p->pmenu.items[i].isToggle = false;
			p->pmenu.items[i].buttonset = NULL;
			j=0;
			while(toggles[j] > 0)
			{
				if(p->pmenu.items[i].action == toggles[j])
				{
					p->pmenu.items[i].isToggle = true;
					break;
				}
				j++;
			}
			p->pmenu.items[i].radioset = NULL;
			p->pmenu.items[i].isRadio = false;
			j=0;
			while(radiosets[j][0] > 0)
			{
				for(k=1;k<=radiosets[j][0];k++)
					if(p->pmenu.items[i].action == radiosets[j][k])
					{
						p->pmenu.items[i].isRadio = true;
						p->pmenu.items[i].radioset = &radiosets[j][0];
						break;
					}
				j++;
			}


			p->pmenu.items[i].height = 32;
			p->pmenu.items[i].width = 32;
			p->pmenu.items[i].lumalpha = MALLOC(GLubyte*, 32 * 32 * 2);
			for(j=0;j<32;j++) //pixel row within image
			{
				for(k=0;k<32;k++) //pixel column within image
				{
					int ibyte, ibit, color;
					if(false){
						//binary image
						ibyte = (j*32 + k)/8;
						ibit  = (j*32 + k)%8;
						color = buttonlist[i][ibyte] & (1<<(7-ibit))? 255 : 0;
					}else if(true){
						//255 alpha channel image
						ibyte = j*32 + k;
						color = buttonlist[i][ibyte];
					}
					p->pmenu.items[i].lumalpha[(j*32 +k)*2 +0] = color;
					p->pmenu.items[i].lumalpha[(j*32 +k)*2 +1] = color;
				}
			}
			//copy to main lumapha
			irow = i / buttonAtlasSizeCol; //button row within 5x5 buttons image
			icol = i % buttonAtlasSizeCol; //button colum "
			for(j=0;j<32;j++) //pixel row within item image
			{
				for(k=0;k<32;k++) //pixel column within item image
				{
					p->pmenu.lumalpha[(irow*32 +j)*32*2*buttonAtlasSizeCol + (icol*32 +k)*2 + 0] = p->pmenu.items[i].lumalpha[(j*32 +k)*2 +0];
					p->pmenu.lumalpha[(irow*32 +j)*32*2*buttonAtlasSizeCol + (icol*32 +k)*2 + 1] = p->pmenu.items[i].lumalpha[(j*32 +k)*2 +1];
				}
			}
			//assign texture coordinates
			p->pmenu.items[i].tex0[0][0] = (GLfloat)(icol*32 + 0)/(GLfloat)(32*buttonAtlasSizeCol);
			p->pmenu.items[i].tex0[1][0] = (GLfloat)(irow*32 + 0)/(GLfloat)(32*buttonAtlasSizeRow);
			p->pmenu.items[i].tex0[0][1] = (GLfloat)(icol*32 +32)/(GLfloat)(32*buttonAtlasSizeCol);
			p->pmenu.items[i].tex0[1][1] = (GLfloat)(irow*32 +32)/(GLfloat)(32*buttonAtlasSizeRow);
			//Q. how will I flexibly do the highlight?
			//I think I would loop through the buttons to do the highlighting, but then the buttons themselves
			//can be done with a single mesh.
			mt = i*2*4;
			kt = 0;
			// 1 3   vertex order
			// 0 2
			for(j=0;j<2;j++) //row 
			{
				for(k=0;k<2;k++) //column
				{
					//texture coords
					//p->pmenu.items[i].tex[kt +0] = p->pmenu.tex[mt+kt + 0] = p->pmenu.items[i].tex0[0][j];
					//p->pmenu.items[i].tex[kt +1] = p->pmenu.tex[mt+kt + 1] = p->pmenu.items[i].tex0[1][k];
					p->pmenu.items[i].tex[kt +0] = p->pmenu.items[i].tex0[0][j];
					p->pmenu.items[i].tex[kt +1] = p->pmenu.items[i].tex0[1][k];
					kt+=2;
				}
			}
		}
		glGenTextures(1, &(p->pmenu.textureID));
	    glBindTexture(GL_TEXTURE_2D, p->pmenu.textureID);
		glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
		glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);

		glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE_ALPHA, 32*buttonAtlasSizeCol, 32*buttonAtlasSizeRow, 0, GL_LUMINANCE_ALPHA , GL_UNSIGNED_BYTE, p->pmenu.lumalpha);

		{
			//fill out any sub-button togglebutton toggleset actions
			int *togset, kset;
			kset = 0;
			togset = togglesets[kset];
			while(togset[0]){
				int j,k, ipact, nact, iact;
				ipact = togset[0];
				nact = togset[1];
				for(k=0;k<p->pmenu.nitems;k++){
					if(ipact == p->pmenu.items[k].action){
						int m;
						p->pmenu.items[k].buttonset = malloc(sizeof(buttonSet));
						p->pmenu.items[k].buttonset->n = nact;
						p->pmenu.items[k].buttonset->index = 0; //first one by default
						p->pmenu.items[k].buttonset->items = malloc(nact * sizeof(void*));
						for(m=0;m<nact;m++){
							int n;
							p->pmenu.items[k].buttonset->items[m] = NULL;
							iact = togset[m+2];
							for(n=0;n<p->pmenu.nitems;n++){
								if(iact == p->pmenu.items[n].action){
									p->pmenu.items[k].buttonset->items[m] = &p->pmenu.items[n];
								}
							}
						}
					}
				}
				nact = togset[1];
				kset++;
				togset = togglesets[kset];
			}
		}


		for(i=0;i<p->pmenu.nbitems;i++)
		{
			int j, k, mi, mv, kv, kt;
			GLfloat dx;
			FXY xyxy[2];
			int bz = p->buttonSize;
		
			//pixel coord boxes, for mouse picking of buttons
			p->pmenu.bitems[i].butrect[0] = 5+(i*bz);	/* lower left  x */
			p->pmenu.bitems[i].butrect[1] = 0;			/* lower left  y */
			p->pmenu.bitems[i].butrect[2] = 5+(i*bz)+bz;/* upper right x */
			p->pmenu.bitems[i].butrect[3] = bz;			/* upper right y */

			mv = i*3*4;
			mi = i*3*2;
			kv = 0;
			kt = 0;
			// 1 3   vertex order
			// 0 2
			/* normalized coords moved to draw function for resize
			xyxy[0] = screen2normalizedScreen( 0.0f, p->pmenu.yoffset); //0.0f);
			//xyxy[1] = screen2normalizedScreen( 32.0f, 32.0f + p->pmenu.yoffset);
			xyxy[1] = screen2normalizedScreen( p->buttonSize, p->buttonSize + p->pmenu.yoffset);
			*/
			xyxy[0].x = 0.0f;
			xyxy[0].y = 0.0f; //we'll need to compute yoffset in draw
			xyxy[1].x = (GLfloat)p->buttonSize;
			xyxy[1].y = (GLfloat)p->buttonSize;
			dx = xyxy[1].x - xyxy[0].x;
			for(j=0;j<2;j++) //row 
			{
				for(k=0;k<2;k++) //column
				{
					//vertex coords
					p->pmenu.bitems[i].vert[kv +0] = p->pmenu.vert[mv+kv +0] = xyxy[j].x + (GLfloat)(i*dx);
					p->pmenu.bitems[i].vert[kv +1] = p->pmenu.vert[mv+kv +1] = xyxy[k].y;
					p->pmenu.bitems[i].vert[kv +2] = p->pmenu.vert[mv+kv +2] = 0.0f;
					//p->pmenu.bitems[i].vert[kv +0] = xyxy[j].x + (GLfloat)(i*dx);
					//p->pmenu.bitems[i].vert[kv +1] = xyxy[k].y;
					//p->pmenu.bitems[i].vert[kv +2] = 0.0f;
					kv+=3;
				}
			}

			// triangle indices
			// 1-3
			// |/|
			// 0-2
			p->pmenu.ind[mi +0] = (GLushort)(i*4) +0;
			p->pmenu.ind[mi +1] = (GLushort)(i*4) +1;
			p->pmenu.ind[mi +2] = (GLushort)(i*4) +3;
			p->pmenu.ind[mi +3] = (GLushort)(i*4) +0;
			p->pmenu.ind[mi +4] = (GLushort)(i*4) +3;
			p->pmenu.ind[mi +5] = (GLushort)(i*4) +2;

			//assign icon+action to menubar button location
			for(j=0;j<p->pmenu.nitems;j++){
				if(mainbar[i] == p->pmenu.items[j].action){
					p->pmenu.bitems[i].item = &p->pmenu.items[j];
					break;
				}
			}
		}
	}
	p->butsLoaded = 1;
}
/* the following setMenuButton_ were defined for some other front end and called from various locations
   - re-using here
   - mar 2015: this still refers-to/indexes-into the longer items[] list, not the shorter mainbuttonbar []  
*/
int getMenuItemByAction(int iaction)
{
	int i;
	ppstatusbar p = (ppstatusbar)gglobal()->statusbar.prv;
	for(i=0;i<p->pmenu.nitems;i++)
		if(p->pmenu.items[i].action == iaction)
			return i;
	return -1;
}

void setRadioPalsOff(int *ipals, int iaction)
{
	int i,j;
	ppstatusbar p;
	ttglobal tg = gglobal();
	p = (ppstatusbar)tg->statusbar.prv;
	j=0;
	for(j=1;j<=ipals[0];j++)
	{
		if(ipals[j] != iaction)
		{
			i = getMenuItemByAction(ipals[j]);
			if(i > -1)
				p->pmenu.items[i].butStatus = 0;
		}
	}
	return;
}

void setMenuButton_collision(int val){	
	int i;
	ppstatusbar p = (ppstatusbar)gglobal()->statusbar.prv;
	i = getMenuItemByAction(ACTION_COLLISION);
	if(i > -1)
		p->pmenu.items[i].butStatus = val;
}

void setMenuButton_texSize(int size){ 
	/* this isn't called in my configuration so I don't know what the range is */
	printf("text size=%d\n",size);
	//int bmfontsize = 2; /* 0,1 or 2 - our current size range*/
}
void setMenuButton_headlight(int val){ 
	int i;
	ppstatusbar p = (ppstatusbar)gglobal()->statusbar.prv;
	i = getMenuItemByAction(ACTION_HEADLIGHT);
	if(i > -1)
		p->pmenu.items[i].butStatus = val;
}
void setMenuButton_shift(int val){
	int i;
	ppstatusbar p = (ppstatusbar)gglobal()->statusbar.prv;
	i = getMenuItemByAction(ACTION_SHIFT);
	if(i > -1)
		p->pmenu.items[i].butStatus = val;
}
void setMenuButton_ctrl(ctrl){
	//not used yet - ctrl affects 3-state buttons like Explore (goes into pick mode when pressed 2x), 
	//and examine, spherical (ctrl + LMB == RMB)
	// could be used to highlight double-pressed button so user knows to toggle off
}

static int chord2action [] = {ACTION_YAWZ,ACTION_YAWPITCH,ACTION_ROLL,ACTION_XY};

void setMenuButton_navModes(int type, int dragchord)
{
	int i, newval, iaction;
	ppstatusbar p = (ppstatusbar)gglobal()->statusbar.prv;

	switch(type)
	{
		case VIEWER_NONE: 
			iaction = ACTION_EXAMINE;
			newval = 0;
			break;
		case VIEWER_EXAMINE: 
			iaction = ACTION_EXAMINE;
			newval = 1;
			break;
		case VIEWER_WALK:
			iaction = ACTION_WALK;
			newval = 1;
			break;
		case VIEWER_TURNTABLE:
			iaction = ACTION_TURNTABLE;
			newval = 1;
			break;
		case VIEWER_LOOKAT:
			iaction = ACTION_LOOKAT;
			newval = 1;
			break;
		case VIEWER_EXPLORE:
			iaction = ACTION_EXPLORE;
			newval = 1;
			break;
		case VIEWER_SPHERICAL:
			iaction = ACTION_SPHERICAL;
			newval = 1;
			break;
		case VIEWER_DIST:
			iaction = ACTION_DIST;
			newval = 1;
			break;
		case VIEWER_FLY:
#if defined(QNX) || defined(KIOSK)//|| defined(_MSC_VER)
			iaction = ACTION_FLY2;
#else
			iaction = ACTION_FLY;
#endif
			newval = 1;
			break;
		default:
			iaction = -1;
	}
	if(iaction > -1){
		i = getMenuItemByAction(iaction);
		if(i>-1){
			if(p->pmenu.items[i].buttonset){
				//its a fancy toggle button that rolls through different icons; need to update the sub-icon
				if(iaction == p->pmenu.items[i].action){
					int j;
					//pressing a button already pressed if its a togglebutton, means incrementing the toggle
					switch(iaction){
						case ACTION_FLY:
							if(p->pmenu.items[i].buttonset){
								for(j=0;j<p->pmenu.items[i].buttonset->n;j++){
									if(p->pmenu.items[i].buttonset->items[j]->action == chord2action[dragchord]){
										p->pmenu.items[i].buttonset->index = j;
									}
								}
							}
							break;
						default:
							break;
					}
				}
			}
			if(p->pmenu.items[i].isRadio)
				setRadioPalsOff(p->pmenu.items[i].radioset,iaction);
			p->pmenu.items[i].butStatus = newval;
		}
	}
return;
}
int viewer_getDragChord();
void viewer_setDragChord(int chord);

/* handle all the displaying and event loop stuff. */
void updateButtonStatus()
{
	//checks collision, headlight and navmode in the model
	//in MVC -model,view,controller- terminology, this is the controller,
	// and it checks the model (libfreewrl ie fwl functions), and updates the view (statusbar hud)
	//-these can be set by either the UI (this statusbar), keyboard hits, or from 
	// events inside vrml. 
	// Here we take our UI current state from the scene state. 
	// For FRONTEND_HANDLES_DISPLAY_THREAD configurations, the frontend should do 
	// the equivalent of the following once per frame (poll state and set UI)
	int headlight, collision, navmode, dragchord, lookatMode, ctrl, shift;
	//poll model state:
	headlight = fwl_get_headlight();
	collision = fwl_getCollision();
	navmode = fwl_getNavMode();
	dragchord = viewer_getDragChord();
	shift = fwl_getShift();
	ctrl = fwl_getCtrl();
	//lookatMode = fwl_getLookatMode();
	//update UI(view):
	setMenuButton_shift(shift);
	setMenuButton_ctrl(ctrl);
	setMenuButton_navModes(navmode,dragchord);
	setMenuButton_headlight(headlight);
	setMenuButton_collision(collision);
	//setMenuButton_lookat(lookatMode);
}

void updateConsoleStatus()
{
	//polls ConsoleMessage.c for accumulated messages and updates statusbarHud.c via hudSetConsoleMessage
	int nlines, i;
	char *buffer;
	nlines = fwg_get_unread_message_count(); //poll model
	for (i = 0; i<nlines; i++)
	{
		buffer = fwg_get_last_message(); // poll model
		hudSetConsoleMessage(buffer); //update UI(view)
		//free(buffer);
	}
}


int handleButtonOver(int mouseX, int mouseY)
{
	/* called from mainloop > fwl_handle_aqua to
	a) detect a button over and
	b) highlight underneath the button*/
	int i, x, y;
	ppstatusbar p;
	ttglobal tg = gglobal();
	p = (ppstatusbar)tg->statusbar.prv;

	x = mouseX;
	if (p->pmenu.top)
		y = mouseY;
	else
		y = p->screenHeight - mouseY - p->pmenu.yoffset;
	p->isOver = -1;
	for (i = 0; i<p->pmenu.nbitems; i++)
	if (x > p->pmenu.bitems[i].butrect[0] && x < p->pmenu.bitems[i].butrect[2]
		&& y > p->pmenu.bitems[i].butrect[1] && y < p->pmenu.bitems[i].butrect[3])
	{
		/* printf("%d",i); */  /* is over */
		p->isOver = i;
		break;
	}
	return p->isOver == -1 ? 0 : 1;
}
char *frontend_pick_URL(void);
char *frontend_pick_file(void);
void toggleMenu(int val)
{
	ppstatusbar p;
	ttglobal tg = gglobal();
	p = (ppstatusbar)tg->statusbar.prv;
	p->showButtons = val > 0 ? 1 : 0;
}

int action2chord(int iaction){
	int ichord = CHORD_YAWZ;
	switch(iaction){
		case ACTION_YAWZ:	ichord = CHORD_YAWZ; break;
		case ACTION_XY:		ichord = CHORD_XY; break;
		case ACTION_YAWPITCH: ichord = CHORD_YAWPITCH; break;
		case ACTION_ROLL:	ichord = CHORD_ROLL; break;
		default: ichord = 0; break;
	}
	return ichord;
}

int handleButtonRelease(int mouseX, int mouseY)
{
	/* called from mainloop > to 
	a) detect a button hit and 
	b) toggle the button icon and
	c) set the related option
	*/
	int i,x,y,ihit,iaction;
    //int j, oldval;
	ppstatusbar p;
	ttglobal tg = gglobal();
	p = (ppstatusbar)tg->statusbar.prv;

	x = mouseX;
	if(p->pmenu.top)
		y = mouseY;
	else
		y = p->screenHeight - mouseY - p->pmenu.yoffset;
	ihit = -1;
	for(i=0;i<p->pmenu.nbitems;i++)
	{
		if(x > p->pmenu.bitems[i].butrect[0] && x < p->pmenu.bitems[i].butrect[2]
		&& y > p->pmenu.bitems[i].butrect[1] && y < p->pmenu.bitems[i].butrect[3] )
		{
			ihit = i;
			iaction = p->pmenu.bitems[i].item->action;
			if(p->pmenu.bitems[i].item->butStatus && p->pmenu.bitems[i].item->buttonset ){
				//fancy toggle button, need to increment to next button in buttonset whenever the button is pressed when already active
				p->pmenu.bitems[i].item->buttonset->index++;
				p->pmenu.bitems[i].item->buttonset->index = (p->pmenu.bitems[i].item->buttonset->index % 4);
			}
			if(p->pmenu.bitems[i].item->isRadio)
			{
				setRadioPalsOff(p->pmenu.bitems[i].item->radioset,iaction);
				if(p->pmenu.bitems[i].item->isToggle )
					p->pmenu.bitems[i].item->butStatus = 1 - p->pmenu.bitems[i].item->butStatus;
				else
					p->pmenu.bitems[i].item->butStatus = 1;
			}
			else if(p->pmenu.bitems[i].item->isToggle)
				p->pmenu.bitems[i].item->butStatus = 1 - p->pmenu.bitems[i].item->butStatus;
			switch(iaction)
			{
				case ACTION_WALK:	
					fwl_set_viewer_type (VIEWER_WALK); break; 
				case ACTION_FLY2:	
					fwl_set_viewer_type (VIEWER_FLY2); break; 
				case ACTION_TILT:	
					fwl_set_viewer_type (VIEWER_TILT); break; 
				case ACTION_TPLANE:	
					fwl_set_viewer_type (VIEWER_TPLANE); break; 
				case ACTION_RPLANE:	
					fwl_set_viewer_type (VIEWER_RPLANE); break; 
				case ACTION_FLY:	
					fwl_set_viewer_type(VIEWER_FLY); 
					if(p->pmenu.bitems[i].item->buttonset){
						int iact, idx, ichord;
						idx = p->pmenu.bitems[i].item->buttonset->index;
						iact = p->pmenu.bitems[i].item->buttonset->items[idx]->action;
						ichord = action2chord(iact);
						viewer_setDragChord(ichord);
					}
					break;
				case ACTION_EXPLORE:
					fwl_set_viewer_type(VIEWER_EXPLORE); break;
				case ACTION_LOOKAT:
					fwl_set_viewer_type(VIEWER_LOOKAT); break;
				case ACTION_EXAMINE:
					fwl_set_viewer_type (VIEWER_EXAMINE); break; 
				case ACTION_SPHERICAL:
					fwl_set_viewer_type(VIEWER_SPHERICAL); break;
				case ACTION_TURNTABLE:
					fwl_set_viewer_type(VIEWER_TURNTABLE); break;
				case ACTION_DIST:
					fwl_set_viewer_type(VIEWER_DIST); break;
				case ACTION_SHIFT:	 fwl_setShift(p->pmenu.bitems[i].item->butStatus); break;
				case ACTION_LEVEL:	 viewer_level_to_bound(); break;
				case ACTION_HEADLIGHT: fwl_toggle_headlight(); break;
				case ACTION_COLLISION: toggle_collision(); break; 
				case ACTION_PREV:	fwl_Prev_ViewPoint(); break;
				case ACTION_NEXT:	fwl_Next_ViewPoint(); break;
				case ACTION_HELP:		
					//p->showHelp = p->pmenu.items[i].butStatus; 
					//break;
				case ACTION_MESSAGES:	
					//p->showConText = p->pmenu.items[i].butStatus; 
					//break;
				case ACTION_OPTIONS: 
					//p->showOptions = p->pmenu.items[i].butStatus; 
					break;
				case ACTION_RELOAD:  fwl_reload(); break;
				case ACTION_URL:
					//load URL
#ifndef KIOSK
					//fwl_setPromptForURL(1);
					#if defined(_MSC_VER) || defined(QNX)
					{
						char *fname = frontend_pick_URL();
						if(fname)
						{
							fwl_replaceWorldNeeded(fname);
							FREE(fname);
						}
					}
					#endif
#endif

					break;
				case ACTION_FILE:
					//load file
#ifndef KIOSK
					//fwl_setPromptForFile(1);

					#if defined(_MSC_VER) || defined(QNX)
					{
						char *fname = frontend_pick_file();
						if(fname)
						{
							fwl_replaceWorldNeeded(fname);
							FREE(fname);
						}
					}
					#endif

#endif
					break;
				default:
					break;
			}
		} //end if rect
	} //end for
#ifdef KIOSK
	return ihit == -1 ? 0 : 1;
#else
	if(ihit == -1) toggleMenu(0);
	return 1;
#endif
}
void updateButtonVertices()
{
	int i,j,k,kv,mv;
	float xx,yy;
    //int zz;
	FXY xy;
	ppstatusbar p;
	ttglobal tg = gglobal();
	p = (ppstatusbar)tg->statusbar.prv;

	//p->pmenu.yoffset = (float) yoff_button; //0.0f;
	if(p->pmenu.top) p->pmenu.yoffset = (float)(p->screenHeight - p->buttonSize - p->pmenu.yoffset); //32.0f;

	for(i=0;i<p->pmenu.nbitems;i++)
	{
		kv = 0;
		for(j=0;j<2;j++)
			for(k=0;k<2;k++)
			{
				xx = p->pmenu.bitems[i].vert[kv +0];
				yy = p->pmenu.bitems[i].vert[kv +1];
				xy = screen2normalizedScreen(xx,yy + p->pmenu.yoffset + p->side_bottom);
				mv = i*3*4;
				p->pmenu.vert[mv+kv +0] = xy.x;
				p->pmenu.vert[mv+kv +1] = xy.y;
				kv += 3;
			}
	}
}

void renderButtons()
{
	/* called from drawStatusBar() to render the user buttons like walk/fly, headlight, collision etc. */
	int i,loaded,ctrl;
	ppstatusbar p;
	ttglobal tg = gglobal();
	p = (ppstatusbar)tg->statusbar.prv;
	

	// get rid of compiler warning
	loaded = 0;

	if(!p->butsLoaded)
		initButtons();
	updateButtonVertices();
	//updateButtonStatus();
	glScissor(0,(int)p->pmenu.yoffset+p->side_bottom,p->screenWidth,p->buttonSize); //tg->Mainloop.clipPlane*2);

	glEnable(GL_SCISSOR_TEST);
	//glClearColor(.922f,.91f,.844f,1.0f); //windowing gray
	glClearColor(colorClear[0],colorClear[1],colorClear[2],colorClear[3]);
	//glClearColor(.754f,.82f,.93f,1.0f); //193.0f/256.0f,210.0f/256.0f,238.0f/256.0f,1.0f); //windowing blue
	glClear(GL_COLOR_BUFFER_BIT);
	glDisable(GL_SCISSOR_TEST);
	doglClearColor(); //set back for other cases
	// Bind the base map
	glActiveTexture ( GL_TEXTURE0 );

	glBindTexture ( GL_TEXTURE_2D, p->pmenu.textureID );

	ctrl = fwl_getCtrl();
	for(i=0;i<p->pmenu.nbitems;i++)
	{
		int do_ctrl;
		GLfloat rgba[4] = {1.0, 1.0, 1.0, 1.0};
		bool highlightIt = p->pmenu.bitems[i].item->butStatus;
		do_ctrl = ctrl && i < 8;

		if(p->pmenu.bitems[i].item->butStatus) 
			rgba[0] = .7f; rgba[1] = .7f; rgba[2] = .7f; //DEPRESSED/TOGGLED BUTTON BACKGROUND COLOR
		if(highlightIt) //i==p->isOver || p->pmenu.items[i].butStatus)
		{
			/*draw a background highlight rectangle*/

			//glUniform4f(p->color4fLoc,rgba[0],rgba[1],rgba[2],rgba[3]); //..8f,.87f,.97f,1.0f);
			if(do_ctrl)
				glUniform4f(p->color4fLoc,colorButtonCTRL[0],colorButtonCTRL[1],colorButtonCTRL[2],colorButtonCTRL[3]);
			else
				glUniform4f(p->color4fLoc,colorButtonHighlight[0],colorButtonHighlight[1],colorButtonHighlight[2],colorButtonHighlight[3]);
			glVertexAttribPointer ( p->positionLoc, 3, GL_FLOAT, 
						GL_FALSE, 0, &(p->pmenu.vert[i*3*4]) );
			// Load the texture coordinate
			glVertexAttribPointer ( p->texCoordLoc, 2, GL_FLOAT,
						GL_FALSE, 0, p->pmenu.items[p->pmenu.nitems-1].tex );   //nitems -1 should be the blank texture
			glEnableVertexAttribArray ( p->positionLoc );
			glEnableVertexAttribArray ( p->texCoordLoc );
			glDrawElements ( GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, p->pmenu.ind ); //first 6 should be 0 1 3 0 3 2
		}
		// render triangles

		glVertexAttribPointer ( p->positionLoc, 3, GL_FLOAT, 
							GL_FALSE, 0, &(p->pmenu.vert[i*3*4]) );

						
		// Load the texture coordinate
		if(p->pmenu.bitems[i].item->buttonset){
			//fancy togglebutton buttonset
			glVertexAttribPointer ( p->texCoordLoc, 2, GL_FLOAT,
							GL_FALSE, 0, p->pmenu.bitems[i].item->buttonset->items[p->pmenu.bitems[i].item->buttonset->index]->tex );   //nitems -1 should be the blank texture
		}else{
			glVertexAttribPointer ( p->texCoordLoc, 2, GL_FLOAT,
							GL_FALSE, 0, p->pmenu.bitems[i].item->tex );   //nitems -1 should be the blank texture
		}
		//glUniform4f(p->color4fLoc,0.37f,0.37f,0.9f,1.0f);  //BLUE ICON SHAPE COLOR
		glUniform4f(p->color4fLoc,colorButtonIcon[0],colorButtonIcon[1],colorButtonIcon[2],colorButtonIcon[3]);
		glEnableVertexAttribArray ( p->positionLoc );
		glEnableVertexAttribArray ( p->texCoordLoc );
		glDrawElements ( GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, p->pmenu.ind ); //first 6 should be 0 1 3 0 3 2

		/* old one-shot
		// Load the vertex position
		glVertexAttribPointer ( p->positionLoc, 3, GL_FLOAT, 
								GL_FALSE, 0, p->pmenu.vert );
		// Load the texture coordinate
		glVertexAttribPointer ( p->texCoordLoc, 2, GL_FLOAT,
								GL_FALSE, 0, p->pmenu.tex );  //fails - p->texCoordLoc is 429xxxxx - garbage
		glUniform4f(p->color4fLoc,0.37f,0.37f,0.9f,1.0f);  //BLUE ICON SHAPE COLOR
		glEnableVertexAttribArray ( p->positionLoc );
		glEnableVertexAttribArray ( p->texCoordLoc );

		// Set the base map sampler to texture unit to 0
		glUniform1i ( p->textureLoc, 0 );
		glDrawElements ( GL_TRIANGLES, p->pmenu.nbitems*3*2, GL_UNSIGNED_SHORT, p->pmenu.ind ); //render the main menu bar
		*/
	}
	//clean up
	FW_GL_BINDBUFFER(GL_ARRAY_BUFFER, 0);
	FW_GL_BINDBUFFER(GL_ELEMENT_ARRAY_BUFFER, 0);
	p->hadString = 1;
}
void statusbarHud_DrawCursor(GLint textureID,int x,int y){
GLfloat cursorVert[] = {
	-.05f, -.05f, 0.0f,
	-.05f,  .05f, 0.0f,
	 .05f,  .05f, 0.0f,
	-.05f, -.05f, 0.0f,
	 .05f,  .05f, 0.0f,
	 .05f, -.05f, 0.0f};
GLfloat cursorTex[] = {
	0.0f, 0.0f,
	0.0f, 1.0f,
	1.0f, 1.0f,
	0.0f, 0.0f,
	1.0f, 1.0f,
	1.0f, 0.0f};
	GLushort ind[] = {0,1,2,3,4,5};
	//GLint pos, tex;
	FXY fxy;
	XY xy;
	int i,j;
	GLfloat cursorVert2[18];

	ppstatusbar p;
	ttglobal tg = gglobal();
	p = (ppstatusbar)tg->statusbar.prv;

	FW_GL_DEPTHMASK(GL_FALSE);
	glDisable(GL_DEPTH_TEST);
	if(p->programObject == 0) initProgramObject();
	glUseProgram ( p->programObject );

	xy = mouse2screen(x,y);
	FW_GL_VIEWPORT(0, 0, p->screenWidth, p->screenHeight);
	fxy = screen2normalizedScreenScale((GLfloat)xy.x,(GLfloat)xy.y);
	fxy.y -= 1.0;
	fxy.x -= 1.0;
	//fxy.y *= .5;
	//fxy.x *= .5;
	for(i=0;i<6;i++){
		for(j=0;j<3;j++)
			cursorVert2[i*3 + j] = cursorVert[i*3 +j];
		cursorVert2[i*3 +0] += fxy.x;
		cursorVert2[i*3 +1] += fxy.y;
	}
	// Load the vertex position
   //p->positionLoc = glGetAttribLocation ( p->programObject, "a_position" );
   //p->texCoordLoc = glGetAttribLocation ( p->programObject, "a_texCoord" );
   // Get the sampler location
   //p->textureLoc = glGetUniformLocation ( p->programObject, "Texture0" );
   //p->color4fLoc = glGetUniformLocation ( p->programObject, "Color4f" );

	glVertexAttribPointer (p->positionLoc, 3, GL_FLOAT, 
						   GL_FALSE, 0, cursorVert2 );
	// Load the texture coordinate
	glVertexAttribPointer ( p->texCoordLoc, 2, GL_FLOAT,
						   GL_FALSE, 0, cursorTex );  //fails - p->texCoordLoc is 429xxxxx - garbage
	glUniform4f(p->color4fLoc,0.7f,0.7f,0.9f,1.0f);
	glEnableVertexAttribArray (p->positionLoc );
	glEnableVertexAttribArray ( p->texCoordLoc);

	//// Bind the base map - see above
	glActiveTexture ( GL_TEXTURE0 );
	glBindTexture ( GL_TEXTURE_2D, textureID );

	// Set the base map sampler to texture unit to 0
	glUniform1i ( p->textureLoc, 0 );
	glDrawElements ( GL_TRIANGLES, 3*2, GL_UNSIGNED_SHORT, ind ); //just render the active ones

	FW_GL_BINDBUFFER(GL_ARRAY_BUFFER, 0);
	FW_GL_BINDBUFFER(GL_ELEMENT_ARRAY_BUFFER, 0);


	glEnable(GL_DEPTH_TEST);
	FW_GL_DEPTHMASK(GL_TRUE);
	restoreGlobalShader();

}
void updateViewCursorStyle(int cstyle);
void fwl_set_frontend_using_cursor(int on);
bool showAction(ppstatusbar p, int action)
{
	int item = getMenuItemByAction(action);
	if( item > -1)
	{
		return p->pmenu.items[item].butStatus;
	}
	return false;
}

int handleStatusbarHud(int mev, int butnum, int mouseX, int mouseY)
{
	int mouseYY;
	ppstatusbar p;
	ttglobal tg = gglobal();
	p = (ppstatusbar)tg->statusbar.prv;

	mouseYY = mouseY; // - p->pmenu.yoffset;
	if ((mev == ButtonPress) || (mev == ButtonRelease))
	{
		/* record which button is down */
		/* >>> statusbar hud */
		int ihit = 0;
		if (p->showButtons)
		{
			if (mev == ButtonRelease)
				ihit = handleButtonRelease(mouseX,mouseYY);
			else
				ihit = 1; //ButtonPress
			//return 1;
		}
		//if(p->showOptions)
		if (!ihit && showAction(p, ACTION_OPTIONS))
		{
			if (mev == ButtonPress)
				ihit = handleOptionPress(mouseX,mouseYY);
			//return 1;
		}
		if (ihit) return 1;
	}
	if (mev == MotionNotify)
	{
		if (p->pmenu.top){
#if defined(KIOSK)
			toggleMenu(1);
#elif defined(_MSC_VER) 
			//if input device is a mouse, mouse over statusbar to bring down menu
			//else call toggleMenu from main program on some window event
			static int lastover;
			if (p->screenHeight - mouseYY < 16)
			{
				if (!lastover)
					toggleMenu(1 - p->showButtons);
				//p->showButtons = 1 - p->showButtons;
				lastover = 1;
			}
			else{
				lastover = 0;
			}
#endif
			if (p->showButtons == 1){
				int ihit;
				updateViewCursorStyle(ACURSE);
				//setArrowCursor();
				ihit = handleButtonOver(mouseX,mouseYY);
				if (ihit) return 1;
				//return 1; /* don't process for navigation */
			}
		}
		else{
			/* buttons at bottom, menu triggered by mouse-over */
			//int clipline;
			//(*clipplane) = p->statusBarSize; //16;
			/* >>> statusbar hud */
			//clipline = p->clipPlane;
			//if (p->showButtons) clipline = p->pmenu.yoffset + p->buttonSize; //2*(*clipplane);
			if (p->screenHeight - mouseY < p->clipPlane) //clipline)
			{
				p->showButtons = 1;
				if( p->screenHeight - mouseYY > 0 ){
					//setArrowCursor();
					handleButtonOver(mouseX,mouseYY);
				}
				updateViewCursorStyle(ACURSE);
				return 1; /* don't process for navigation */
			}
			else
			{
				p->showButtons = 0;
			}
		}
		//if(p->showOptions)
		if (showAction(p, ACTION_OPTIONS))
		{
			/* let HUD options menu swallow button clicks */
			return 1;
		}
		/* <<< statusbar hud */
	}
	return 0;
}
// call a few functions from the display event handlers, such as resize
void statusbar_set_window_size(int width, int height)
{
	ttglobal tg = gglobal();
	ppstatusbar p = (ppstatusbar)tg->statusbar.prv;
	p->screenHeight = height;
	p->screenWidth = width;
	fwl_setScreenDim(width, height);
}
void statusbar_handle_mouse(int mev, int butnum, int mouseX, int mouseY)
{
	ttglobal tg = gglobal();
	ppstatusbar p = (ppstatusbar)tg->statusbar.prv;
	if (!handleStatusbarHud(mev, butnum, mouseX, mouseY)){
		fwl_set_frontend_using_cursor(FALSE);
		fwl_handle_aqua(mev, butnum, mouseX, mouseY); /* ,gcWheelDelta); */
	}else{
		fwl_set_frontend_using_cursor(TRUE);
	}
}
char *getMessageBar(); //in common.c
char *fwl_getKeyChord();
void fwl_setClipPlane(int height);
void drawStatusBarSide()
{
}
void update_pinned(){
	ppstatusbar p;
	ttglobal tg = gglobal();
	p = (ppstatusbar)tg->statusbar.prv;
	fwl_get_sbh_pin(&p->statusbar_pinned,&p->menubar_pinned);
}

void drawStatusBar() 
{
	/* drawStatusBar() is called just before swapbuffers in mainloop so anything that you want to render 2D
	   (non-scene things like browser status messages FPS, 
	   browser control option buttons (menu bar) and checkboxes, console error messages)
	   you can put in here.
	   Ideally things like buttons and status could be hidden/optional/configurable, since some
	   applications don't want to give option control to the user - for example a museum kiosk application -
	   and some applications have gui widgets for it.
	
	The interface that statusbarHud implements
	let S be statusbar, M be menubar, C be console and H be options+help
S	drawStatusBar() - call before swapbuffers in mainloop

	The interface statusbarHud requires other modules to implement to serve it:
	//already implemented
MH	int currentX[0],currentY[0] - mouse coords
CH	int screenHeight - in pixels
H	Viewer.(various stereo params)
	//new requirements for statusbarHud:
H	void setStereo(int type);							//implement in viewer.c
H	void toggleOrSetStereo(int type);					// "
H	void setAnaglyphSideColor(char color, int iside);	//"
H	void updateEyehalf();								//" 
M	viewer_level_to_bound();							//"
M       void toggle_collision()                             //"
    */
	char *pp; 
	int i,nsides, vrml_clipplane;
	GLfloat side_bottom_f;
	ppstatusbar p;
	ttglobal tg = gglobal();
	p = (ppstatusbar)tg->statusbar.prv;

	//init-once things are done everytime for convenience
	//fwl_setClipPlane(p->statusBarSize);
	if(!p->fontInitialized) initFont();
	update_ui_colors();
	update_pinned();
	if(p->programObject == 0) initProgramObject();

	//MVC statusbarHud is in View and Controller just called us and told us 
	//..to poll the Model to update and draw ourself
	updateButtonStatus();  //poll Model for some button state
	updateConsoleStatus(); //poll Model for console text

	glDepthMask(GL_FALSE);
	glDisable(GL_DEPTH_TEST);
	p->posType = 1; // use RasterPos2i instead of WindowPos2i
	glUseProgram ( p->programObject );
	glViewport(0, 0, p->screenWidth, p->screenHeight);
   
	//p->showButtons = 1;
	//p->menubar_pinned = 0;
	//p->statusbar_pinned = 1;
	p->show_status = (p->statusbar_pinned && !p->showButtons) || (!p->statusbar_pinned && p->showButtons) || (p->menubar_pinned); // || showAction(p, ACTION_OPTIONS);
	p->show_menu = p->menubar_pinned || p->showButtons;
	p->yoff_status = 0;
	p->pmenu.yoffset = (p->menubar_pinned || !p->statusbar_pinned) ? p->statusBarSize : 0;

	p->clipPlane = (p->show_menu ?  p->buttonSize : 0) + p->statusBarSize; //(p->show_status ? p->statusBarSize : 0);
	//int mouseplane = p->showButtons ? p->clipPlane : p->statusBarSize;
	vrml_clipplane = (p->statusbar_pinned ? p->statusBarSize : 0) + (p->menubar_pinned? p->buttonSize : 0);
	fwl_setClipPlane(vrml_clipplane); //p->clipPlane);

	nsides = 1;
	if (Viewer()->updown) nsides = 2; //one stereo mode updown draws the menubar and/or statusbar twice, once for each stereo side
	for (i = 0; i < nsides; i++)
	{
		p->side_top = 0;
		p->side_bottom = 0;
		side_bottom_f = -1.0f;
		if (Viewer()->updown){
			//the upper viewport is the left stereo side is i==0
			p->side_top = i*(p->screenHeight / 2);
			p->side_bottom = (1 -i) *(p->screenHeight /2);
			if(i == 0) side_bottom_f = 0.0f;
		}

		if (p->show_menu) //p->showButtons)
		{
			renderButtons();
#ifndef KIOSK
			glDepthMask(GL_TRUE);
			if (p->posType == 1) {
				glEnable(GL_DEPTH_TEST);
			}
			//continue;
#endif
		}
		if(p->show_status)
		{

			/* OK time to update the status bar */
			/* unconditionally clear the statusbar area */
			glScissor(0, p->side_bottom, p->screenWidth, p->statusBarSize); //p->clipPlane);
			glEnable(GL_SCISSOR_TEST);
			//glClearColor(.922f, .91f, .844f, 1.0f); //windowing gray
			glClearColor(colorClear[0],colorClear[1],colorClear[2],colorClear[3]);
			glClear(GL_COLOR_BUFFER_BIT);
			glDisable(GL_SCISSOR_TEST);

			// you must call drawStatusBar() from render() just before swapbuffers 
			glDepthMask(FALSE);
			glDisable(GL_DEPTH_TEST);

			//glUniform4f(p->color4fLoc, .2f, .2f, .2f, 1.0f);
			glUniform4f(p->color4fLoc,colorStatusbarText[0],colorStatusbarText[1],colorStatusbarText[2],colorStatusbarText[3]);
			{
				FXY xy;
				xy = screen2normalizedScreenScale((GLfloat)p->bmWH.x, (GLfloat)p->bmWH.y);
				pp = get_status(); // p->buffer;
				/* print status bar text - things like PLANESENSOR */
				printString2(-1.0f + xy.x*5.0f, side_bottom_f, pp);
				p->hadString = 1;
			}
			{
				int len;
				char *strfps, *strstatus, *strAkeys;
				FXY xy;
				xy = screen2normalizedScreenScale((GLfloat)p->bmWH.x, (GLfloat)p->bmWH.y);
				strfps = getMessageBar();
				strstatus = &strfps[15];
				printString2(-1.0f + xy.x*25.0f, side_bottom_f, strfps);
				printString2(-1.0f + xy.x*35.0f, side_bottom_f, strstatus);
				strAkeys = fwl_getKeyChord();
				len = strlen(strAkeys);
				printString2(1.0f - xy.x*len, side_bottom_f, strAkeys);
			}

		}

		//glUniform4f(p->color4fLoc, 1.0f, 1.0f, 1.0f, 1.0f);
		glUniform4f(p->color4fLoc,colorMessageText[0],colorMessageText[1],colorMessageText[2],colorMessageText[3]);
		glDisable(GL_SCISSOR_TEST); //ideally this would scissor everything but statusbar+menu so we don't overwrite bars
		if (showAction(p, ACTION_HELP))
			printKeyboardHelp(p);
		if (showAction(p, ACTION_MESSAGES))
			printConsoleText();
		if (showAction(p, ACTION_OPTIONS))
			printOptions();
	}
	glClearColor(0.0f,0.0f,0.0f,1.0f); 
	glDepthMask(TRUE);
	glEnable(GL_DEPTH_TEST);
}
#endif
