From 074e60afa5b1756af07e7bfad0e910155388812b Mon Sep 17 00:00:00 2001
From: Allanis <allanis@saracraft.net>
Date: Mon, 18 Feb 2013 17:12:35 +0000
Subject: [PATCH] [Add] Finished the basic toolkit by rounding off corners,
 adding gl_printMax and gl_printMid. [Add] Use flag system with gl_screen.  
 -- Optional AA for primitives.

---
 src/conf.c    |  17 ++-
 src/land.c    |   2 -
 src/opengl.c  | 140 +++++++++++++++++++---
 src/opengl.h  |  35 +++++-
 src/player.c  |  46 +++----
 src/toolkit.c | 325 +++++++++++++++++++++++++++++++++++++++++++++-----
 src/toolkit.h |   3 +
 7 files changed, 482 insertions(+), 86 deletions(-)

diff --git a/src/conf.c b/src/conf.c
index e12691c..95b64c3 100644
--- a/src/conf.c
+++ b/src/conf.c
@@ -65,7 +65,7 @@ void conf_setDefaults(void) {
   // GL.
   gl_screen.w = 800;
   gl_screen.h = 640;
-  gl_screen.fullscreen = 0;
+  gl_screen.flags = 0;
   // Joystick.
   indjoystick = -1;
   namjoystick = NULL;
@@ -75,7 +75,7 @@ void conf_setDefaults(void) {
 
 // Ok.. Parse a config file plox.
 int conf_loadConfig(const char* file) {
-  int i;
+  int i = 0;
 
   lua_State* L = luaL_newstate();
   if(luaL_dofile(L, file) == 0) {
@@ -86,7 +86,16 @@ int conf_loadConfig(const char* file) {
     // OpenGL properties..
     conf_loadInt("width", gl_screen.w);
     conf_loadInt("height", gl_screen.h);
-    conf_loadBool("fullscreen", gl_screen.fullscreen);
+    conf_loadBool("fullscreen", i);
+    if(i)gl_screen.flags |= OPENGL_FULLSCREEN;
+    conf_loadBool("aa", i);
+    if(i)gl_screen.flags |= OPENGL_AA_POINT | OPENGL_AA_LINE || OPENGL_AA_POLYGON;
+    conf_loadBool("aa_point", i);
+    if(i)gl_screen.flags |= OPENGL_AA_POINT;
+    conf_loadBool("aa_line", i)
+    if(i)gl_screen.flags |= OPENGL_AA_LINE;
+    conf_loadBool("aa_polygon", i);
+    if(i)gl_screen.flags |= OPENGL_AA_POLYGON;
     conf_loadBool("showfps", show_fps);
     conf_loadInt("maxfps", max_fps);
 
@@ -178,7 +187,7 @@ void conf_parseCLI(int argc, char** argv) {
   while((c = getopt_long(argc, argv, "fF:d:J:j:hv", long_options, &option_index)) != -1) {
     switch(c) {
       case 'f':
-        gl_screen.fullscreen = 1;
+        gl_screen.flags |= OPENGL_FULLSCREEN;
         break;
       case 'F':
         max_fps = atoi(optarg);
diff --git a/src/land.c b/src/land.c
index 7f56c2b..1c9e6b3 100644
--- a/src/land.c
+++ b/src/land.c
@@ -12,7 +12,6 @@ void land(Planet* p) {
   if(landed) return;
 
   planet = p;
-  pause();
   land_wid = window_create(-1, -1, 400, 300);
   window_addButton(land_wid, 400-80-20, 20, 80, 40, "takeoff", "Takeoff", (void(*)(char*))takeoff);
   landed = 1;
@@ -23,7 +22,6 @@ void takeoff(void) {
   if(!landed) return;
 
   planet = NULL;
-  unpause();
   window_destroy(land_wid);
   landed = 0;
 }
diff --git a/src/opengl.c b/src/opengl.c
index 69558d9..0beeab7 100644
--- a/src/opengl.c
+++ b/src/opengl.c
@@ -19,11 +19,21 @@
 #define FONT_DEF "../gfx/fonts/font.ttf"
 
 // Default colors.
-glColour cLightGrey   = { .r = 0.80, .g = 0.80, .b = 0.80, .a = 1 };
-glColour cGrey        = { .r = 0.65, .g = 0.65, .b = 0.65, .a = 1 };
-glColour cDarkGrey    = { .r = 0.50, .g = 0.50, .b = 0.50, .a = 1 };
-glColour cGreen       = { .r = 0.20, .g = 0.80, .b = 0.20, .a = 1 };
-glColour cRed         = { .r = 0.80, .g = 0.20, .b = 0.20, .a = 1 };
+glColour cWhite     = { .r = 1.00, .g = 1.00, .b = 1.00, .a = 1 };
+glColour cGrey90    = { .r = 0.90, .g = 0.90, .b = 0.90, .a = 1 };
+glColour cGrey80    = { .r = 0.80, .g = 0.80, .b = 0.80, .a = 1 };
+glColour cGrey70    = { .r = 0.70, .g = 0.70, .b = 0.70, .a = 1 };
+glColour cGrey60    = { .r = 0.60, .g = 0.60, .b = 0.60, .a = 1 };
+glColour cGrey50    = { .r = 0.50, .g = 0.50, .b = 0.50, .a = 1 };
+glColour cGrey40    = { .r = 0.40, .g = 0.40, .b = 0.40, .a = 1 };
+glColour cGrey30    = { .r = 0.30, .g = 0.30, .b = 0.30, .a = 1 };
+glColour cGrey20    = { .r = 0.20, .g = 0.20, .b = 0.20, .a = 1 };
+glColour cGrey10    = { .r = 0.10, .g = 0.10, .b = 0.10, .a = 1 };
+glColour cBlack     = { .r = 0.00, .g = 0.00, .b = 0.00, .a = 1 };
+
+glColour cGreen     = { .r = 0.20, .g = 0.80, .b = 0.20, .a = 1 };
+glColour cRed       = { .r = 0.80, .g = 0.20, .b = 0.20, .a = 1 };
+
 
 // offsets to Adjust the pilot's place onscreen to be in the middle, even with the GUI.
 extern double gui_xoff;
@@ -461,6 +471,109 @@ void gl_print(const glFont* ft_font, const double x, const double y,
   glDisable(GL_TEXTURE_2D);
 }
 
+// Acts just like gl_print, but prints to a max length of max.
+// Return the amount of characters we had to suppress.
+int gl_printMax(const glFont* ft_font, const int max, const double x, const double y,
+      const glColour* c, const char* fmt, ...) {
+  //float h = ft_font->h / .63; // Slightly increases font size.
+  char text[256];
+  va_list ap;
+  int i, n, len, ret;
+
+  if(ft_font == NULL) ft_font = &gl_defFont;
+
+  if(fmt == NULL) return -1;
+  else {
+    // convert the symbols to text.
+    va_start(ap, fmt);
+    vsprintf(text, fmt, ap);
+    va_end(ap);
+  }
+  
+  // Limit the size.
+  len = (int)strlen(text);
+  for(n = 0, i = 0; i < len; i++) {
+    n += ft_font->w[(int)text[i]];
+    if(n > max) {
+      ret = len - i; // Difference.
+      text[i] = '\0';
+      break;
+    }
+  }
+
+  // Display the text.
+  glEnable(GL_TEXTURE_2D);
+
+  glListBase(ft_font->list_base);
+
+  glMatrixMode(GL_MODELVIEW); // Projection gets full fast using modelview.
+  glPushMatrix(); // Translation matrix.
+  glTranslated(x - (double)gl_screen.w/2., y - (double)gl_screen.h/2., 0);
+
+  if(c == NULL) glColor4d(1., 1., 1., 1.);
+  else COLOUR(*c);
+  glCallLists(i, GL_UNSIGNED_BYTE, &text);
+
+  glPopMatrix(); // Translation matrix.
+  glDisable(GL_TEXTURE_2D);
+
+  return ret;
+}
+
+// Acts just like gl_printMax, but centers the text in the width.
+int gl_printMid(const glFont* ft_font, const int width, double x, const double y,
+      const glColour* c, const char* fmt, ...) {
+  //float h = ft_font->h / .63; // Slightly increases font size.
+  char text[256];
+  va_list ap;
+  int i, n, len, ret;
+  
+  ret = 0; // Default return value.
+
+  if(ft_font == NULL) ft_font = &gl_defFont;
+
+  if(fmt == NULL) return -1;
+  else {
+    // convert the symbols to text.
+    va_start(ap, fmt);
+    vsprintf(text, fmt, ap);
+    va_end(ap);
+  }
+  
+  // Limit the size.
+  len = (int)strlen(text);
+  for(n = 0, i = 0; i < len; i++) {
+    n += ft_font->w[(int)text[i]];
+    if(n > width) {
+      ret = len - i; // Difference.
+      n -= ft_font->w[(int)text[i]]; // Actual size.
+      text[i] = '\0';
+      break;
+    }
+  }
+
+  x += (double)(width-n)/2.;
+
+  // Display the text.
+  glEnable(GL_TEXTURE_2D);
+
+  glListBase(ft_font->list_base);
+
+  glMatrixMode(GL_MODELVIEW); // Projection gets full fast using modelview.
+  glPushMatrix(); // Translation matrix.
+  glTranslated(x - (double)gl_screen.w/2., y - (double)gl_screen.h/2., 0);
+
+  if(c == NULL) glColor4d(1., 1., 1., 1.);
+  else COLOUR(*c);
+  glCallLists(i, GL_UNSIGNED_BYTE, &text);
+
+  glPopMatrix(); // Translation matrix.
+  glDisable(GL_TEXTURE_2D);
+
+  return ret;
+}
+
+
 // Get the width of the text about to be printed.
 int gl_printWidth(const glFont* ft_font, const char* fmt, ...) {
   int i, n;
@@ -621,10 +734,10 @@ void gl_freeFont(glFont* font) {
 
 // Initialize SDL/OpenGL etc.
 int gl_init(void) {
-  int depth, i, supported = 0;
+  int doublebuf, depth, i, supported = 0;
   SDL_Rect** modes;
   int flags = SDL_OPENGL;
-  flags |= SDL_FULLSCREEN * gl_screen.fullscreen;
+  flags |= SDL_FULLSCREEN * (gl_has(OPENGL_FULLSCREEN) ? 1: 0);
 
   // Initializes video.
   if(SDL_InitSubSystem(SDL_INIT_VIDEO) < 0) {
@@ -632,12 +745,8 @@ int gl_init(void) {
     return -1;
   }
 
-  // FFUUUU Ugly cursor thing.
-  // -- Ok, Maybe for now.
-  //SDL_ShowCursor(SDL_DISABLE);
-
   // Get available fullscreen modes.
-  if(gl_screen.fullscreen) {
+  if(gl_has(OPENGL_FULLSCREEN)) {
     modes = SDL_ListModes(NULL, SDL_OPENGL | SDL_FULLSCREEN);
     if(modes == NULL) { // Could happen, but rare.
       WARN("No fullscreen modes available");
@@ -689,14 +798,15 @@ int gl_init(void) {
   SDL_GL_GetAttribute(SDL_GL_GREEN_SIZE,    &gl_screen.g);
   SDL_GL_GetAttribute(SDL_GL_BLUE_SIZE,     &gl_screen.b);
   SDL_GL_GetAttribute(SDL_GL_ALPHA_SIZE,    &gl_screen.a);
-  SDL_GL_GetAttribute(SDL_GL_DOUBLEBUFFER,  &gl_screen.doublebuf);
+  SDL_GL_GetAttribute(SDL_GL_DOUBLEBUFFER,  &doublebuf);
+  if(doublebuf) gl_screen.flags |= OPENGL_DOUBLEBUF;
   gl_screen.depth = gl_screen.r + gl_screen.g + gl_screen.b + gl_screen.a;
 
   // Debug heaven.
   DEBUG("OpenGL Window Created: %dx%d@%dbpp %s", gl_screen.w, gl_screen.h, gl_screen.depth,
-        gl_screen.fullscreen ? "fullscreen" : "window");
+        (gl_has(OPENGL_FULLSCREEN)) ? "fullscreen" : "window");
   DEBUG("r: %d, g: %d, b: %d, a: %d, doublebuffer: %s", gl_screen.r, gl_screen.g, gl_screen.b, gl_screen.a,
-        gl_screen.doublebuf ? "yes" : "no");
+        (gl_has(OPENGL_DOUBLEBUF)) ? "yes" : "no");
   DEBUG("Renderer: %s", glGetString(GL_RENDERER));
 
   // Some openGL options.
diff --git a/src/opengl.h b/src/opengl.h
index c7481a7..4216b56 100644
--- a/src/opengl.h
+++ b/src/opengl.h
@@ -18,12 +18,17 @@
 #define RGBMASK RMASK,GMASK,BMASK,AMASK
 
 // Info about opengl screen.
+#define OPENGL_FULLSCREEN (1<<0)
+#define OPENGL_DOUBLEBUF  (1<<1)
+#define OPENGL_AA_POINT   (1<<2)
+#define OPENGL_AA_LINE    (1<<3)
+#define OPENGL_AA_POLYGON (1<<4)
+#define gl_has(f)         (gl_screen.flags & (f)) // Check for the flag.
 typedef struct {
   int w, h;         // Window dimensions.
   int depth;        // Depth in bpp.
-  int fullscreen;   // 1 = fullscreen, 0 = windowed.
   int r, g, b, a;   // Framebuffer values in bits.
-  int doublebuf;    // Double buffer.
+  int flags;        // Store different properties.
 } glInfo;
 extern glInfo gl_screen; // Local structure set with gl_init etc.
 
@@ -34,9 +39,21 @@ typedef struct {
 #define COLOUR(x) glColor4d((x).r, (x).g, (x).b, (x).a)
 
 // Default colors.
-extern glColour cLightGrey;
-extern glColour cGrey;
-extern glColour cDarkGrey;
+// -- Greyscale.
+extern glColour cWhite;
+#define cGrey   cGrey70
+extern glColour cBlack;
+
+extern glColour cGrey90;
+extern glColour cGrey80;
+extern glColour cGrey70;
+extern glColour cGrey60;
+extern glColour cGrey50;
+extern glColour cGrey40;
+extern glColour cGrey30;
+extern glColour cGrey20;
+extern glColour cGrey10;
+
 extern glColour cGreen;
 extern glColour cRed;
 
@@ -76,8 +93,16 @@ void gl_blitSprite(const glTexture* sprite, const Vec2* pos,
 
 void gl_blitStatic(const glTexture* texture, const Vec2* pos, const glColour* c);
 void gl_bindCamera(const Vec2* pos);
+
 void gl_print(const glFont* ft_font, const double x, const double y,
       const glColour* c, const char* fmt, ...);
+
+int gl_printMax(const glFont* ft_font, const int max, const double x, const double y,
+      const glColour* c, const char* fmt, ...);
+
+int gl_printMid(const glFont* ft_font, const int width, double x, const double y,
+      const glColour* c, const char* fmt, ...);
+
 int  gl_printWidth(const glFont* ft_font, const char* fmt, ...);
 
 // Initialize/cleanup.
diff --git a/src/player.c b/src/player.c
index 63a6db1..174ea8a 100644
--- a/src/player.c
+++ b/src/player.c
@@ -311,21 +311,16 @@ void player_render(void) {
   // Nav.
   if(planet_target >= 0) {
     // Planet landing target.
-    i = gl_printWidth(NULL, "Land");
-    gl_print(NULL, gui.nav.x + (gui.nav.w - i)/2.,
-          gui.nav.y - 5, &cConsole, "Land");
-    i = gl_printWidth(&gui.smallFont, "%s", cur_system->planets[planet_target].name);
-    gl_print(&gui.smallFont, gui.nav.x + (gui.nav.w - i)/2.,
-          gui.nav.y - 10 - gui.smallFont.h, NULL,
-          "%s", cur_system->planets[planet_target].name);  
+    gl_printMid(NULL, (int)gui.nav.w, gui.nav.x, gui.nav.y - 5, &cConsole, "Land");
+
+    gl_printMid(&gui.smallFont, (int)gui.nav.w, gui.nav.x, gui.nav.y - 10 - gui.smallFont.h,
+          NULL, "%s", cur_system->planets[planet_target].name);
   }
   else if(planet_target == -1) {
     // No planet target.
-    i = gl_printWidth(NULL, "Navigation");
-    gl_print(NULL, gui.nav.x + (gui.nav.w - i)/2.,
+    gl_printMid(NULL, (int)gui.nav.w, gui.nav.x,
           gui.nav.y - 5, &cConsole, "Navigation");
-    i = gl_printWidth(&gui.smallFont, "Off");
-    gl_print(&gui.smallFont, gui.nav.x + (gui.nav.w - i)/2.,
+    gl_printMid(&gui.smallFont, (int)gui.nav.w, gui.nav.x,
           gui.nav.y - 10 - gui.smallFont.h, &cGrey, "Off");
   }
 
@@ -336,39 +331,34 @@ void player_render(void) {
   
   // Weapon.
   if(player->secondary == NULL) {
-    i = gl_printWidth(NULL, "Secondary");
-    gl_print(NULL, gui.weapon.x + (gui.weapon.w - i)/2.,
+    gl_printMid(NULL, (int)gui.weapon.w, gui.weapon.x,
           gui.weapon.y - 5, &cConsole, "Secondary");
-    i = gl_printWidth(&gui.smallFont, "None");
-    gl_print(&gui.smallFont, gui.weapon.x + (gui.weapon.w - i)/2.,
+    gl_printMid(&gui.smallFont, (int)gui.weapon.w,  gui.weapon.x,
           gui.weapon.y - 10 - gl_defFont.h, &cGrey, "None");
   } else {
     f = &gl_defFont;
     if(player->ammo == NULL) {
       i = gl_printWidth(f, "%s", player->secondary->outfit->name);
-      if(i > gui.weapon.w) {
+      if(i > (int)gui.weapon.w)
         // Font is too big.
         f = &gui.smallFont;
-        i = gl_printWidth(f, "%s", player->secondary->outfit->name);
-      }
-      gl_print(f, gui.weapon.x + (gui.weapon.w - i)/2.,
+
+      gl_printMid(f, (int)gui.weapon.w,  gui.weapon.x,
             gui.weapon.y - (gui.weapon.h - f->h)/2.,
             &cConsole, "%s", player->secondary->outfit->name);
     } else {
       // Use the ammunitions name.
       i = gl_printWidth(f, "%s", player->secondary->outfit->name);
-      if(i > gui.weapon.w) {
+      if(i > gui.weapon.w)
         // Font is too big.
         f = &gui.smallFont;
-        i = gl_printWidth(f, "%s", player->ammo->outfit->name);
-      }
-      gl_print(f, gui.weapon.x + (gui.weapon.w - i)/2.,
+
+      gl_printMid(f, (int)gui.weapon.w, gui.weapon.x,
             gui.weapon.y - 5,
             &cConsole, "%s", player->ammo->outfit->name);
 
       // Print ammo underneath to the left.
-      i = gl_printWidth(&gui.smallFont, "%d", player->ammo->quantity);
-      gl_print(&gui.smallFont, gui.weapon.x + (gui.weapon.w - i)/2.,
+      gl_printMid(&gui.smallFont, gui.weapon.w, gui.weapon.x,
             gui.weapon.y - 10 - gl_defFont.h,
             NULL, "%d", player->ammo->quantity);
     }
@@ -401,10 +391,9 @@ void player_render(void) {
             NULL, "%s: %.0f%%", "Armor", p->armour/p->armour_max*100.);
   } else {
     // No target.
-    i = gl_printWidth(NULL, "No Target");
-    gl_print(NULL, gui.target.x + (SHIP_TARGET_W - i)/2.,
+    gl_printMid(NULL, SHIP_TARGET_W, gui.target.x,
           gui.target.y + (SHIP_TARGET_H - gl_defFont.h)/2.,
-          &cGrey, "No Target");
+          &cGrey80, "No Target");
   }
 
   // Misc.
@@ -891,6 +880,7 @@ void player_screenshot(void) {
   char filename[20];
   // TODO not overwirte old screenshots.
   strncpy(filename, "screenshot.png", 20);
+  DEBUG("SCREENSHOT!");
   gl_screenshot(filename);
 }
 
diff --git a/src/toolkit.c b/src/toolkit.c
index 473ded7..9407665 100644
--- a/src/toolkit.c
+++ b/src/toolkit.c
@@ -23,9 +23,19 @@ typedef struct {
   double w,h; // Dimensions.
 
   WidgetStatus status;
-
-  void(*fptr) (char*); // Callback.
-  char* string; // Stored text.
+  
+  union {
+    // Widget button.
+    struct {
+      void(*fptr) (char*); // Callback.
+      char* string; // Stored text.
+    };
+    // Widget text.
+    struct {
+      glFont* font;
+      glColour* colour;
+    };
+  };
 } Widget;
 
 typedef struct {
@@ -52,24 +62,42 @@ static void widget_cleanup(Widget* widget);
 // Render.
 static void window_render(Window* w);
 static void toolkit_renderButton(Widget* btn, double bx, double by);
+static void toolkit_renderText(Widget* txt, double bx, double by);
 
 // Add a button that when pressed will trigger call, passing it's name as the
 // only parameter.
 void window_addButton(const unsigned int wid, const int x, const int y, const int w,
       const int h, char* name, char* display, void (*call)(char*)) {
   
-  Widget* widget = window_newWidget(wid);
+  Widget* wgt = window_newWidget(wid);
 
-  widget->type = WIDGET_BUTTON;
-  widget->name = strdup(name);
-  widget->string = strdup(display);
+  wgt->type = WIDGET_BUTTON;
+  wgt->name = strdup(name);
+  wgt->string = strdup(display);
 
   // Set the properties.
-  widget->x = (double) x;
-  widget->y = (double) y;
-  widget->w = (double) w;
-  widget->h = (double) h;
-  widget->fptr = call;
+  wgt->x = (double) x;
+  wgt->y = (double) y;
+  wgt->w = (double) w;
+  wgt->h = (double) h;
+  wgt->fptr = call;
+}
+
+void window_addText(const unsigned int wid, const int x, const int y, const int w,
+      const int h, char* name, glFont* font, glColour* colour) {
+  Widget* wgt = window_newWidget(wid);
+
+  wgt->type = WIDGET_TEXT;
+  wgt->name = strdup(name); // Display the widgets name.
+
+  // Set the properties.
+  wgt->x      = (double) x;
+  wgt->y      = (double) y;
+  wgt->w      = (double) w;
+  wgt->h      = (double) h;
+  wgt->font   = font;
+  wgt->colour = colour;
+
 }
 
 // Return pointer to newly allocated widget.
@@ -109,8 +137,8 @@ unsigned int window_create(const int x, const int y, const int w, const int h) {
   windows[nwindows].h = (double) h;
   if((x == -1) && (y == -1)) {
     // Center.
-    windows[nwindows].x = windows[nwindows].w/2.;
-    windows[nwindows].y = windows[nwindows].h/2.;
+    windows[nwindows].x = gl_screen.w/2. - windows[nwindows].w/2.;
+    windows[nwindows].y = gl_screen.h/2. - windows[nwindows].h/2.;
   } else {
     windows[nwindows].x = (double) x;
     windows[nwindows].y = (double) y;
@@ -159,6 +187,7 @@ void window_destroy(unsigned int wid) {
       // No windows left.
       SDL_ShowCursor(SDL_DISABLE);
       toolkit = 0; // Disable the toolkit.
+      if(paused) unpause();
     }
 }
 
@@ -166,18 +195,163 @@ void window_destroy(unsigned int wid) {
 static void window_render(Window* w) {
   int i;
   double x, y;
+  glColour *lc, *c, *dc, *oc;
 
+  // Position.
   x = w->x - (double)gl_screen.w/2.;
   y = w->y - (double)gl_screen.h/2.;
 
-  // Window background.
-  glBegin(GL_QUADS);
-    COLOUR(cLightGrey);
+  // Colours.
+  lc  = &cGrey90;
+  c   = &cGrey70;
+  dc  = &cGrey50;
+  oc  = &cGrey30;
 
-    glVertex2d(x,         y);
-    glVertex2d(x + w->w,  y);
-    glVertex2d(x + w->w,  y + w->h);
-    glVertex2d(x,         y + w->h);
+  // Window shaded background.
+  // Main body.
+  glShadeModel(GL_SMOOTH);
+  glBegin(GL_QUADS);
+    COLOUR(*dc);
+    glVertex2d(x+21.,       y);
+    glVertex2d(x+w->w-21.,  y);
+
+    COLOUR(*c);
+    glVertex2d(x+w->w-21, y+0.6*w->h);
+    glVertex2d(x+21,      y+0.6*w->h);
+  glEnd();
+
+  glShadeModel(GL_FLAT);
+  glBegin(GL_QUADS);
+    COLOUR(*c);
+    glVertex2d(x+21.,       y+0.6*w->h);
+    glVertex2d(x+w->w-21.,  y+0.6*w->h);
+    glVertex2d(x+w->w-21.,  y+w->h);
+    glVertex2d(x+21.,       y+w->h);
+  glEnd();
+
+  glShadeModel(GL_SMOOTH);
+  // Left side.
+  glBegin(GL_POLYGON);
+    COLOUR(*c);
+    glVertex2d(x+21., y+0.6*w->h); // Center.
+    COLOUR(*dc);
+    glVertex2d(x+21., y);
+    glVertex2d(x+15., y+1.);
+    glVertex2d(x+10., y+3.);
+    glVertex2d(x+6.,  y+6.);
+    glVertex2d(x+3.,  y+10.);
+    glVertex2d(x+1.,  y+15.);
+    glVertex2s(x,     y+21.);
+    COLOUR(*c);
+    glVertex2d(x,     y+0.6*w->h); // Front of center.
+    glVertex2d(x,     y+w->h-21.);
+    glVertex2d(x+1.,  y+w->h-15.);
+    glVertex2d(x+3.,  y+w->h-10.);
+    glVertex2d(x+6.,  y+w->h-6.);
+    glVertex2d(x+10., y+w->h-3.);
+    glVertex2d(x+15., y+w->h-1.);
+    glVertex2d(x+21., y+w->h);
+  glEnd();
+  // Right side.
+  glBegin(GL_POLYGON);
+    COLOUR(*c);
+    glVertex2d(x+w->w-21., y+0.6*w->h); // Center.
+    COLOUR(*dc);
+    glVertex2d(x+w->w-21.,  y);
+    glVertex2d(x+w->w-15.,  y+1.);
+    glVertex2d(x+w->w-10.,  y+3.);
+    glVertex2d(x+w->w-6.,   y+6.);
+    glVertex2d(x+w->w-3.,   y+10.);
+    glVertex2d(x+w->w-1.,   y+15.);
+    glVertex2d(x+w->w,      y+21.);
+    COLOUR(*c);
+    glVertex2d(x+w->w,      y+0.6*w->h); // Front of center.
+    glVertex2d(x+w->w,      y+w->h-21.);
+    glVertex2d(x+w->w-1.,   y+w->h-15.);
+    glVertex2d(x+w->w-3.,   y+w->h-10.);
+    glVertex2d(x+w->w-6.,   y+w->h-6.);
+    glVertex2d(x+w->w-10.,  y+w->h-3.);
+    glVertex2d(x+w->w-15.,  y+w->h-1.);
+    glVertex2d(x+w->w-21.,  y+w->h);
+  glEnd();
+
+  // Inner outline.
+  glShadeModel(GL_SMOOTH);
+  glBegin(GL_LINE_LOOP);
+    // Left side.
+    COLOUR(*c);
+    glVertex2d(x+21.,   y);
+    glVertex2d(x+15.,   y+1.);
+    glVertex2d(x+10.,   y+3.);
+    glVertex2d(x+6.,    y+6.);
+    glVertex2d(x+3.,    y+10.);
+    glVertex2d(x+1.,    y+15.);
+    glVertex2d(x,       y+21.);
+    COLOUR(*lc);
+    glVertex2d(x,       y+0.6*w->h); // Front of center.
+    glVertex2d(x,       y+w->h-21.);
+    glVertex2d(x+1.,    y+w->h-15.);
+    glVertex2d(x+3.,    y+w->h-10.);
+    glVertex2d(x+6.,    y+w->h-6.);
+    glVertex2d(x+10.,   y+w->h-2.);
+    glVertex2d(x+15.,   y+w->h-1.);
+    glVertex2d(x+21.,   y+w->h);
+    // Switch to right via top.
+    glVertex2d(x+w->w-21.,  y+w->h);
+    glVertex2d(x+w->w-15.,  y+w->h-1.);
+    glVertex2d(x+w->w-10.,  y+w->h-3.);
+    glVertex2d(x+w->w-6.,   y+w->h-6.);
+    glVertex2d(x+w->w-2.,   y+w->h-10.);
+    glVertex2d(x+w->w-1.,   y+w->h-15.);
+    glVertex2d(x+w->w,      y+w->h-21.);
+    glVertex2d(x+w->w,      y+0.6*w->h); // Front of center.
+    COLOUR(*c);
+    glVertex2d(x+w->w,      y+21.);
+    glVertex2d(x+w->w-1.,   y+15.);
+    glVertex2d(x+w->w-3.,   y+10.);
+    glVertex2d(x+w->w-6.,   y+6.);
+    glVertex2d(x+w->w-10.,  y+3.);
+    glVertex2d(x+w->w-15.,  y+1.);
+    glVertex2d(x+w->w-21.,  y);
+    glVertex2d(x+21.,       y);
+  glEnd();
+
+  // Outter outline.
+  glShadeModel(GL_SMOOTH);
+  glBegin(GL_LINE_LOOP);
+    // Left side.
+    COLOUR(*oc);
+    glVertex2d(x+21.-1.,    y-1.);
+    glVertex2d(x+15.-1.,    y+1.-1.);
+    glVertex2d(x+10.-1.,    y+3.-1.);
+    glVertex2d(x+6.-1.,     y+6.-1.);
+    glVertex2d(x+3.-1.,     y+10.-1.);
+    glVertex2d(x+1.-1.,     y+15.-1.);
+    glVertex2d(x-1.,        y+21.-1.);
+    glVertex2d(x-1.,        y+0.6*w->h); // Front of center.
+    glVertex2d(x-1.,        y+w->h-21.+1.);
+    glVertex2d(x+1.-1.,     y+w->h-15.+1.);
+    glVertex2d(x+3.-1.,     y+w->h-10.+1.);
+    glVertex2d(x+6.-1.,     y+w->h-6.+1.);
+    glVertex2d(x+10.-1.,    y+w->h-3.+1.);
+    glVertex2d(x+15.-1.,    y+w->h-1.+1.);
+    glVertex2d(x+21.-1.,    y+w->h+1.);
+    // Switch to right via top.
+    glVertex2d(x+w->w-21.+1.,   y+w->h+1.);
+    glVertex2d(x+w->w-15.+1.,   y+w->h-1.+1.);
+    glVertex2d(x+w->w-10.+1.,   y+w->h-3.+1.);
+    glVertex2d(x+w->w-6.+1.,    y+w->h-6.+1.);
+    glVertex2d(x+w->w-3.+1.,    y+w->h-10.+1.);
+    glVertex2d(x+w->w-1.+1.,    y+w->h-15.+1.);
+    glVertex2d(x+w->w+1.,       y+w->h-21.+1.);
+    glVertex2d(x+w->w+1.,       y+0.6*w->h);  // Front of center.
+    glVertex2d(x+w->w-1.+1.,    y+21.-1.);
+    glVertex2d(x+w->w-3.+1.,    y+15.-1.);
+    glVertex2d(x+w->w-6.+1.,    y+6.-1.);
+    glVertex2d(x+w->w-10.+1.,   y+3.-1.);
+    glVertex2d(x+w->w-15.+1.,   y+1.-1.);
+    glVertex2d(x+w->w-21.+1.,   y-1.);
+    glVertex2d(x+21.-1.,        y-1.); // Back to beginning.
   glEnd();
 
   // Widgets.
@@ -189,13 +363,14 @@ static void window_render(Window* w) {
         toolkit_renderButton(&w->widgets[i], x, y);
         break;
       case WIDGET_TEXT:
+        toolkit_renderText(&w->widgets[i], x, y);
         break;
     }
   }
 }
 
 static void toolkit_renderButton(Widget* btn, double bx, double by) {
-  glColour* c;
+  glColour* c, *dc, *oc, *lc;
   double x, y;
   int j;
   
@@ -204,32 +379,118 @@ static void toolkit_renderButton(Widget* btn, double bx, double by) {
 
   switch(btn->status) {
     // Set the color.
-    case WIDGET_STATUS_NORMAL:    c = &cDarkGrey; break;
-    case WIDGET_STATUS_MOUSEOVER: c = &cGrey;     break;
-    case WIDGET_STATUS_MOUSEDOWN: c = &cGreen;    break;
+    case WIDGET_STATUS_NORMAL:
+      lc  = &cGrey80;
+      c   = &cGrey60;
+      dc  = &cGrey40;
+      oc  = &cGrey20;
+      break;
+    case WIDGET_STATUS_MOUSEOVER:
+      lc  = &cWhite;
+      c   = &cGrey80;
+      dc  = &cGrey60;
+      oc  = &cGrey40;
+      break;
+    case WIDGET_STATUS_MOUSEDOWN:
+      lc  = &cGreen;
+      c   = &cGreen;
+      dc  = &cGrey40;
+      oc  = &cGrey20;
+      break;
   }
 
+  // Shaded base.
   glShadeModel(GL_SMOOTH);
   glBegin(GL_QUADS);
+    COLOUR(*dc);
+    glVertex2d(x,         y+2/3*btn->h);
+    glVertex2d(x+btn->w,  y+2/3*btn->h);
     COLOUR(*c);
-    glVertex2d(x,           y + 2/3*btn->h);
-    glVertex2d(x + btn->w,  y + 2/3*btn->h);
-    glVertex2d(x + btn->w,  y + btn->h);
-    glVertex2d(x,           y + btn->h);
+    glVertex2d(x+btn->w,  y+0.6*btn->h);
+    glVertex2d(x,         y+0.6*btn->h);
   glEnd();
 
-  j = gl_printWidth(NULL, btn->string);
-  gl_print(NULL, 
-        bx + (double)gl_screen.w/2. + btn->x + (btn->w - (double)j)/2,
+  glShadeModel(GL_FLAT);
+  glBegin(GL_QUADS);
+    COLOUR(*c);
+    glVertex2d(x,         y+0.6*btn->h);
+    glVertex2d(x+btn->w,  y+0.6*btn->h);
+    glVertex2d(x+btn->w,  y+btn->h);
+    glVertex2d(x,         y+btn->h);
+  glEnd();
+
+  // Inner outline.
+  glShadeModel(GL_SMOOTH);
+  // Left.
+  glBegin(GL_LINES);
+    COLOUR(*c);
+    glVertex2d(x, y);
+    COLOUR(*lc);
+    glVertex2d(x, y+btn->h);
+  glEnd();
+  // Right.
+  glBegin(GL_LINES);
+    COLOUR(*c);
+    glVertex2d(x+btn->w, y);
+    COLOUR(*lc);
+    glVertex2d(x+btn->w, y+btn->h);
+  glEnd();
+
+  glShadeModel(GL_FLAT);
+  // Bottom.
+  glBegin(GL_LINES);
+    COLOUR(*c);
+    glVertex2d(x,         y);
+    glVertex2d(x+btn->w,  y);
+  glEnd();
+  // Top.
+  glBegin(GL_LINES);
+    COLOUR(*lc);
+    glVertex2d(x,         y+btn->h);
+    glVertex2d(x+btn->w,  y+btn->h);
+  glEnd();
+
+  // Outter outline.
+  glBegin(GL_LINE_LOOP);
+    COLOUR(cBlack);
+    // Left.
+    glVertex2d(x-1.,      y);
+    glVertex2d(x-1.,      y+btn->h);
+    // Top.
+    glVertex2d(x,         y+btn->h+1.);
+    glVertex2d(x+btn->w,  y+btn->h+1.);
+    // Right.
+    glVertex2d(x+btn->w+1., y+btn->h);
+    glVertex2d(x+btn->w+1., y);
+    // Bottom.
+    glVertex2d(x+btn->w,  y-1.);
+    glVertex2d(x,         y-1.);
+  glEnd();
+
+  gl_printMid(NULL, (int)btn->w,
+        bx + (double)gl_screen.w/2. + btn->x,
         by + (double)gl_screen.h/2. + btn->y + (btn->h - gl_defFont.h)/2.,
         &cRed, btn->string);
 }
 
+static void toolkit_renderText(Widget* txt, double bx, double by) {
+  gl_printMax(txt->font, txt->w, bx+(double)gl_screen.w/2. + txt->x,
+        by + (double)gl_screen.h/2. + txt->y, txt->colour, txt->name);
+}
+
 // Render the window.
 void toolkit_render(void) {
   int i;
+
+  if(gl_has(OPENGL_AA_LINE))    glEnable(GL_LINE_SMOOTH);
+  if(gl_has(OPENGL_AA_POLYGON)) glEnable(GL_POLYGON_SMOOTH);
+
   for(i = 0; i < nwindows; i++)
     window_render(&windows[i]);
+  
+  if(gl_has(OPENGL_AA_LINE))    glDisable(GL_LINE_SMOOTH);
+  if(gl_has(OPENGL_AA_POLYGON)) glDisable(GL_POLYGON_SMOOTH);
+
 }
 
 // Input.
diff --git a/src/toolkit.h b/src/toolkit.h
index 8b98af0..b601057 100644
--- a/src/toolkit.h
+++ b/src/toolkit.h
@@ -11,6 +11,9 @@ void window_addButton(const unsigned int wid, const int x, const int y,
       const int w, const int h, char* name, char* display,
       void(*call)(char*));
 
+void window_addText(const unsigned int wid, const int x, const int y,
+      const int w, const int h, char* name, glFont* font, glColour* colour);
+
 // Destroy window.
 void window_destroy(const unsigned int wid);