changeset 569:d401f87f89f7 trunk

[svn] - added Audacious OSD, yet-another-written-from-scratch plugin to display OSD, based on Ghosd library; currently untied from configure, to compile it you have to run make in its directory; will be added to configure after some testing
author giacomo
date Mon, 29 Jan 2007 06:40:04 -0800
parents 8c64b5abdcda
children 9774ac406bde
files ChangeLog src/aosd/Makefile src/aosd/aosd.c src/aosd/aosd.h src/aosd/aosd_cfg.c src/aosd/aosd_cfg.h src/aosd/aosd_common.h src/aosd/aosd_osd.c src/aosd/aosd_osd.h src/aosd/aosd_style.c src/aosd/aosd_style.h src/aosd/aosd_style_private.h src/aosd/aosd_ui.c src/aosd/aosd_ui.c.old src/aosd/aosd_ui.h src/aosd/ghosd-internal.h src/aosd/ghosd-license src/aosd/ghosd-main.c src/aosd/ghosd-text.c src/aosd/ghosd-text.h src/aosd/ghosd.c src/aosd/ghosd.h
diffstat 22 files changed, 3202 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Mon Jan 29 03:34:57 2007 -0800
+++ b/ChangeLog	Mon Jan 29 06:40:04 2007 -0800
@@ -1,3 +1,11 @@
+2007-01-29 11:34:57 +0000  William Pitcock <nenolod@sacredspiral.co.uk>
+  revision [1222]
+  - fix seek() routine for InputPlayback API.
+  
+  trunk/src/tta/aud-tta.c |    2 +-
+  1 file changed, 1 insertion(+), 1 deletion(-)
+
+
 2007-01-29 11:33:15 +0000  William Pitcock <nenolod@sacredspiral.co.uk>
   revision [1220]
   - update to new plugin API
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/aosd/Makefile	Mon Jan 29 06:40:04 2007 -0800
@@ -0,0 +1,18 @@
+include ../../mk/rules.mk
+include ../../mk/init.mk
+
+OBJECTIVE_LIBS = libaosd$(SHARED_SUFFIX)
+
+noinst_HEADERS = aosd.h aosd_osd.h aosd_style.h aosd_ui.h aosd_cfg.h aosd_common.h ghosd.h ghosd-text.h ghosd-internal.h
+
+LIBDIR = $(plugindir)/$(GENERAL_PLUGIN_DIR)
+
+LIBADD = $(GTK_LIBS)
+SOURCES = aosd.c aosd_osd.c aosd_style.c aosd_ui.c aosd_cfg.c ghosd.c ghosd-text.c ghosd-main.c
+
+OBJECTS = ${SOURCES:.c=.o}
+
+CFLAGS += $(PICFLAGS) $(GTK_CFLAGS) \
+	-I../../intl -I../.. -I..
+
+include ../../mk/objective.mk
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/aosd/aosd.c	Mon Jan 29 06:40:04 2007 -0800
@@ -0,0 +1,140 @@
+/*
+*
+* Author: Giacomo Lozito <james@develia.org>, (C) 2005-2007
+*
+* This program is free software; you can redistribute it and/or modify it
+* under the terms of the GNU General Public License as published by the
+* Free Software Foundation; either version 2 of the License, or (at your
+* option) any later version.
+*
+* This program 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 this program; if not, write to the Free Software Foundation, Inc.,
+* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
+*
+*/
+
+#include "aosd.h"
+#include "aosd_osd.h"
+#include "aosd_cfg.h"
+#include <audacious/input.h>
+#include <audacious/playlist.h>
+#include <audacious/strings.h>
+
+
+static guint timeout_sid = 0;
+static gchar *prev_title = NULL;
+static gboolean was_playing = FALSE;
+aosd_cfg_t * global_config = NULL;
+
+
+gboolean
+aosd_check_pl_change ( gpointer data )
+{
+  if ( ip_data.playing )
+  {
+    Playlist *active = playlist_get_active();
+    gint pos = playlist_get_position(active);
+    gchar *title = playlist_get_songtitle(active, pos);
+
+    if   ( ( title != NULL ) &&
+         ( (( prev_title != NULL ) && ( strcmp(title,prev_title) )) ||
+           ( was_playing == FALSE ) ) )
+    {
+      /* string formatting is done here a.t.m. - TODO - improve this area */
+      gchar *utf8_title = str_to_utf8( title );
+      gchar *utf8_title_markup = g_markup_printf_escaped(
+        "<span font_desc='%s'>%s</span>" , global_config->osd->text.fonts_name[0] , utf8_title );
+      aosd_display( utf8_title_markup , global_config->osd , FALSE );
+      g_free( utf8_title_markup );
+      g_free( utf8_title );
+    }
+
+    if ( prev_title != NULL )
+      g_free(prev_title);
+    prev_title = g_strdup(title);
+
+    g_free( title );
+  }
+  else
+  {
+    if ( prev_title != NULL )
+      { g_free(prev_title); prev_title = NULL; }
+  }
+
+  was_playing = ip_data.playing;
+  return TRUE;
+}
+
+
+/* ***************** */
+/* plug-in functions */
+
+GeneralPlugin *get_gplugin_info()
+{
+   return &aosd_gp;
+}
+
+
+void
+aosd_init ( void )
+{
+  g_log_set_handler( NULL , G_LOG_LEVEL_WARNING , g_log_default_handler , NULL );
+
+  global_config = aosd_cfg_new();
+  aosd_cfg_load( global_config );
+
+  timeout_sid = g_timeout_add( 500 , (GSourceFunc)aosd_check_pl_change , NULL );
+  return;
+}
+
+
+void
+aosd_cleanup ( void )
+{
+  if ( timeout_sid > 0 )
+    g_source_remove( timeout_sid );
+
+  if ( prev_title != NULL )
+  {
+    g_free(prev_title);
+    prev_title = NULL;
+  }
+
+  aosd_shutdown();
+
+  if ( global_config != NULL )
+  {
+    aosd_cfg_delete( global_config );
+    global_config = NULL;
+  }
+
+  return;
+}
+
+
+void
+aosd_configure ( void )
+{
+  /* create a new configuration object */
+  aosd_cfg_t *cfg = aosd_cfg_new();
+  /* fill it with information from config file */
+  aosd_cfg_load( cfg );
+  /* call the configuration UI */
+  aosd_ui_configure( cfg );
+  /* delete configuration object */
+  aosd_cfg_delete( cfg );
+  return;
+}
+
+
+void
+aosd_about ( void )
+{
+  aosd_ui_about();
+  return;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/aosd/aosd.h	Mon Jan 29 06:40:04 2007 -0800
@@ -0,0 +1,45 @@
+/*
+*
+* Author: Giacomo Lozito <james@develia.org>, (C) 2005-2007
+*
+* This program is free software; you can redistribute it and/or modify it
+* under the terms of the GNU General Public License as published by the
+* Free Software Foundation; either version 2 of the License, or (at your
+* option) any later version.
+*
+* This program 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 this program; if not, write to the Free Software Foundation, Inc.,
+* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
+*
+*/
+
+#ifndef _I_AOSD_H
+#define _I_AOSD_H 1
+
+#include "aosd_common.h"
+#include <glib.h>
+#include <audacious/plugin.h>
+
+void aosd_init ( void );
+void aosd_cleanup ( void );
+void aosd_configure ( void );
+void aosd_about ( void );
+
+GeneralPlugin aosd_gp =
+{
+    NULL,						/* handle */
+    NULL,						/* filename */
+    -1,						/* session */
+    "Audacious OSD " AOSD_VERSION_PLUGIN,	/* description */
+    aosd_init,					/* init */
+    aosd_about,					/* about */
+    aosd_configure,				/* configure */
+    aosd_cleanup					/* cleanup */
+};
+
+#endif /* !_I_AOSD_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/aosd/aosd_cfg.c	Mon Jan 29 06:40:04 2007 -0800
@@ -0,0 +1,386 @@
+/*
+*
+* Author: Giacomo Lozito <james@develia.org>, (C) 2005-2007
+*
+* This program is free software; you can redistribute it and/or modify it
+* under the terms of the GNU General Public License as published by the
+* Free Software Foundation; either version 2 of the License, or (at your
+* option) any later version.
+*
+* This program 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 this program; if not, write to the Free Software Foundation, Inc.,
+* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
+*
+*/
+
+#include "aosd_cfg.h"
+#include "aosd_style.h"
+#include <glib.h>
+#include <stdlib.h>
+#include <audacious/configdb.h>
+
+
+static gint
+aosd_cfg_util_str_to_color ( gchar * str , aosd_color_t * color )
+{
+  /* color strings are in format "x,x,x,x", where x are numbers
+     that represent respectively red, green, blue and alpha values (0-65535) */
+  gchar **str_values = g_strsplit( str , "," , 4 );
+  gint col_values[4] = { 0 , 0 , 0, 65535 };
+  gint i = 0;
+  while ( str_values[i] != NULL )
+  {
+    col_values[i] = (gint)strtol( str_values[i] , NULL , 10 );
+    i++;
+  }
+  g_strfreev( str_values );
+  color->red = col_values[0];
+  color->green = col_values[1];
+  color->blue = col_values[2];
+  color->alpha = col_values[3];
+  if ( i < 4 )
+    return -1;
+  else
+    return 0;
+}
+
+
+static gint
+aosd_cfg_util_color_to_str ( aosd_color_t color , gchar ** str )
+{
+  /* color strings are in format "x,x,x,x", where x are numbers
+     that represent respectively red, green, blue and alpha values (0-65535) */
+  *str = g_strdup_printf( "%i,%i,%i,%i" , color.red , color.green , color.blue , color.alpha );
+  if ( *str != NULL )
+    return 0;
+  else
+    return -1;
+}
+
+
+aosd_cfg_t *
+aosd_cfg_new ( void )
+{
+  aosd_cfg_t *cfg = g_malloc0(sizeof(aosd_cfg_t));
+  aosd_cfg_osd_t *cfg_osd = aosd_cfg_osd_new();
+  cfg->set = FALSE;
+  cfg->osd = cfg_osd;
+  return cfg;
+}
+
+
+void
+aosd_cfg_delete ( aosd_cfg_t * cfg )
+{
+  if ( cfg != NULL )
+  {
+    if ( cfg->osd != NULL )
+      aosd_cfg_osd_delete( cfg->osd );
+    g_free( cfg );
+  }
+  return;
+}
+
+
+aosd_cfg_osd_t *
+aosd_cfg_osd_new( void )
+{
+  aosd_cfg_osd_t *cfg_osd = g_malloc0(sizeof(aosd_cfg_osd_t));
+  cfg_osd->decoration.colors = g_array_sized_new( FALSE , TRUE , sizeof(aosd_color_t) ,
+                                                  aosd_deco_style_get_max_numcol() );
+  return cfg_osd;
+}
+
+
+void
+aosd_cfg_osd_delete ( aosd_cfg_osd_t * cfg_osd )
+{
+  if ( cfg_osd != NULL )
+  {
+    gint i = 0;
+    /* free configuration fields */
+    for ( i = 0 ; i < AOSD_TEXT_FONTS_NUM ; i++ )
+    {
+      if ( cfg_osd->text.fonts_name[i] != NULL )
+        g_free( cfg_osd->text.fonts_name[i] );
+    }
+    if ( cfg_osd->decoration.skin_file != NULL )
+      g_free( cfg_osd->decoration.skin_file );
+    if ( cfg_osd->decoration.colors != NULL )
+      g_array_free( cfg_osd->decoration.colors , TRUE );
+  }
+  g_free( cfg_osd );
+  return;
+}
+
+
+/* makes a copy of a aosd_cfg_osd_t object (mostly used by aosd_display) */
+aosd_cfg_osd_t *
+aosd_cfg_osd_copy ( aosd_cfg_osd_t * cfg_osd )
+{
+  aosd_cfg_osd_t *cfg_osd_copy = aosd_cfg_osd_new();
+  gint i = 0;
+  /* copy information */
+  cfg_osd_copy->position.placement = cfg_osd->position.placement;
+  cfg_osd_copy->position.offset_x = cfg_osd->position.offset_x;
+  cfg_osd_copy->position.offset_y = cfg_osd->position.offset_y;
+  cfg_osd_copy->animation.timing_display = cfg_osd->animation.timing_display;
+  cfg_osd_copy->animation.timing_fadein = cfg_osd->animation.timing_fadein;
+  cfg_osd_copy->animation.timing_fadeout = cfg_osd->animation.timing_fadeout;
+  for ( i = 0 ; i < AOSD_TEXT_FONTS_NUM ; i++ )
+  {
+    cfg_osd_copy->text.fonts_name[i] = g_strdup( cfg_osd->text.fonts_name[i] );
+    cfg_osd_copy->text.fonts_color[i] = cfg_osd->text.fonts_color[i];
+    cfg_osd_copy->text.fonts_draw_shadow[i] = cfg_osd->text.fonts_draw_shadow[i];
+    cfg_osd_copy->text.fonts_shadow_color[i] = cfg_osd->text.fonts_shadow_color[i];
+  }
+  cfg_osd_copy->decoration.code = cfg_osd->decoration.code;
+  cfg_osd_copy->decoration.skin_file = g_strdup( cfg_osd->decoration.skin_file );
+  for ( i = 0 ; i < cfg_osd->decoration.colors->len ; i++ )
+  {
+    aosd_color_t color = g_array_index( cfg_osd->decoration.colors , aosd_color_t , i );
+    g_array_insert_val( cfg_osd_copy->decoration.colors , i , color );
+  }
+  return cfg_osd_copy;
+}
+
+
+#ifdef DEBUG
+void
+aosd_cfg_debug ( aosd_cfg_t * cfg )
+{
+  gint i = 0;
+  g_print("\n***** debug configuration *****\n\n");
+  g_print("POSITION\n");
+  g_print("  placement: %i\n", cfg->osd->position.placement);
+  g_print("  offset x: %i\n", cfg->osd->position.offset_x);
+  g_print("  offset y: %i\n", cfg->osd->position.offset_y);
+  g_print("\nANIMATION\n");
+  g_print("  timing display: %i\n", cfg->osd->animation.timing_display);
+  g_print("  timing fade in: %i\n", cfg->osd->animation.timing_fadein);
+  g_print("  timing fade out: %i\n", cfg->osd->animation.timing_fadeout);
+  g_print("\nTEXT\n");
+  for ( i = 0 ; i < AOSD_TEXT_FONTS_NUM ; i++ )
+  {
+    g_print("  font %i: %s\n", i, cfg->osd->text.fonts_name[i]);
+    g_print("  font color %i: %i,%i,%i (alpha %i)\n", i,
+      cfg->osd->text.fonts_color[i].red, cfg->osd->text.fonts_color[i].green,
+      cfg->osd->text.fonts_color[i].blue, cfg->osd->text.fonts_color[i].alpha);
+    g_print("  font %i use shadow: %i\n", i, cfg->osd->text.fonts_draw_shadow[i]);
+    g_print("  font %i shadow color: %i,%i,%i (alpha %i)\n", i,
+      cfg->osd->text.fonts_shadow_color[i].red, cfg->osd->text.fonts_shadow_color[i].green,
+      cfg->osd->text.fonts_shadow_color[i].blue, cfg->osd->text.fonts_shadow_color[i].alpha);
+  }
+  g_print("\nDECORATION\n");
+  g_print("  code: %i\n", cfg->osd->decoration.code);
+  g_print("  custom skin file: %s\n", cfg->osd->decoration.skin_file);
+  for ( i = 0 ; i < cfg->osd->decoration.colors->len ; i++ )
+  {
+    aosd_color_t color = g_array_index( cfg->osd->decoration.colors , aosd_color_t , i );
+    g_print("  color %i: %i,%i,%i (alpha %i)\n", i, color.red, color.green, color.blue, color.alpha);
+  }
+  g_print("\nEXTRA\n");
+  g_print("  set: %i\n", cfg->set);
+  g_print("\n*******************************\n\n");
+  return;
+}
+#endif
+
+
+gint
+aosd_cfg_load ( aosd_cfg_t * cfg )
+{
+  ConfigDb *cfgfile = bmp_cfg_db_open();
+  gint i = 0;
+  gint max_numcol;
+
+  /* position */
+  if ( !bmp_cfg_db_get_int( cfgfile , "aosd" ,
+       "position_placement" , &(cfg->osd->position.placement) ) )
+    cfg->osd->position.placement = AOSD_POSITION_PLACEMENT_TOPLEFT;
+
+  if ( !bmp_cfg_db_get_int( cfgfile , "aosd" ,
+       "position_offset_x" , &(cfg->osd->position.offset_x) ) )
+    cfg->osd->position.offset_x = 0;
+
+  if ( !bmp_cfg_db_get_int( cfgfile , "aosd" ,
+       "position_offset_y" , &(cfg->osd->position.offset_y) ) )
+    cfg->osd->position.offset_y = 0;
+
+  /* animation */
+  if ( !bmp_cfg_db_get_int( cfgfile , "aosd" ,
+       "animation_timing_display" , &(cfg->osd->animation.timing_display) ) )
+    cfg->osd->animation.timing_display = 3000;
+
+  if ( !bmp_cfg_db_get_int( cfgfile , "aosd" ,
+       "animation_timing_fadein" , &(cfg->osd->animation.timing_fadein) ) )
+    cfg->osd->animation.timing_fadein = 300;
+
+  if ( !bmp_cfg_db_get_int( cfgfile , "aosd" ,
+       "animation_timing_fadeout" , &(cfg->osd->animation.timing_fadeout) ) )
+    cfg->osd->animation.timing_fadeout = 300;
+
+  /* text */
+  for ( i = 0 ; i < AOSD_TEXT_FONTS_NUM ; i++ )
+  {
+    gchar *color_str = NULL;
+    gchar *key_str = NULL;
+    key_str = g_strdup_printf( "text_fonts_name_%i" , i );
+    if ( !bmp_cfg_db_get_string( cfgfile , "aosd" , key_str , &(cfg->osd->text.fonts_name[i]) ) )
+      cfg->osd->text.fonts_name[i] = g_strdup( "Sans 26" );
+    g_free( key_str );
+    key_str = g_strdup_printf( "text_fonts_color_%i" , i );
+    if ( !bmp_cfg_db_get_string( cfgfile , "aosd" , key_str , &color_str ) )
+      color_str = g_strdup( "65535,65535,65535,65535" ); /* white , alpha 100% */
+    aosd_cfg_util_str_to_color( color_str , &(cfg->osd->text.fonts_color[i]) );
+    g_free( key_str );
+    g_free( color_str );
+    key_str = g_strdup_printf( "text_fonts_draw_shadow_%i" , i );
+    if ( !bmp_cfg_db_get_bool( cfgfile , "aosd" , key_str , &(cfg->osd->text.fonts_draw_shadow[i]) ) )
+      cfg->osd->text.fonts_draw_shadow[i] = TRUE;
+    g_free( key_str );
+    key_str = g_strdup_printf( "text_fonts_shadow_color_%i" , i );
+    if ( !bmp_cfg_db_get_string( cfgfile , "aosd" , key_str , &color_str ) )
+      color_str = g_strdup( "0,0,0,32767" ); /* black , alpha 50% */
+    aosd_cfg_util_str_to_color( color_str , &(cfg->osd->text.fonts_shadow_color[i]) );
+    g_free( key_str );
+    g_free( color_str );
+  }
+
+  /* decoration */
+  if ( !bmp_cfg_db_get_int( cfgfile , "aosd" ,
+       "decoration_code" , &(cfg->osd->decoration.code) ) )
+    cfg->osd->decoration.code = aosd_deco_style_get_first_code();
+
+  if ( !bmp_cfg_db_get_string( cfgfile , "aosd" ,
+       "decoration_skin_file" , &(cfg->osd->decoration.skin_file) ) )
+    cfg->osd->decoration.skin_file = g_strdup( "" );
+
+  /* decoration - colors */
+  max_numcol = aosd_deco_style_get_max_numcol();
+  for ( i = 0 ; i < max_numcol ; i++ )
+  {
+    gchar *key_str = NULL;
+    gchar *color_str = NULL;
+    aosd_color_t color;
+    key_str = g_strdup_printf( "decoration_color_%i" , i );
+    if ( !bmp_cfg_db_get_string( cfgfile , "aosd" , key_str , &color_str ) )
+    {
+      /* we have different default values for the decoration colors */
+      switch ( i )
+      {
+        case 0:
+          color_str = g_strdup( "0,0,65535,32767" ); /* blue , alpha 50% */
+          break;
+        case 1:
+          color_str = g_strdup( "65535,65535,65535,65535" ); /* white , alpha 100% */
+          break;
+        case 2:
+          color_str = g_strdup( "51400,51400,51400,65535" ); /* gray , alpha 100% */
+          break;
+        default:
+          color_str = g_strdup( "51400,51400,51400,65535" ); /* gray , alpha 100% */
+          break;
+      }
+    }
+    aosd_cfg_util_str_to_color( color_str , &color );
+    g_array_insert_val( cfg->osd->decoration.colors , i , color );
+  }
+
+  bmp_cfg_db_close( cfgfile );
+
+  /* the config object has been filled with information */
+  cfg->set = TRUE;
+
+  return 0;
+}
+
+
+gint
+aosd_cfg_save ( aosd_cfg_t * cfg )
+{
+  ConfigDb *cfgfile = bmp_cfg_db_open();
+  gint i = 0;
+  gint max_numcol;
+
+  if ( cfg->set == FALSE )
+    return -1;
+
+  /* position */
+  bmp_cfg_db_set_int( cfgfile , "aosd" ,
+    "position_placement" , cfg->osd->position.placement );
+
+  bmp_cfg_db_set_int( cfgfile , "aosd" ,
+    "position_offset_x" , cfg->osd->position.offset_x );
+
+  bmp_cfg_db_set_int( cfgfile , "aosd" ,
+    "position_offset_y" , cfg->osd->position.offset_y );
+
+  /* animation */
+  bmp_cfg_db_set_int( cfgfile , "aosd" ,
+    "animation_timing_display" , cfg->osd->animation.timing_display );
+
+  bmp_cfg_db_set_int( cfgfile , "aosd" ,
+    "animation_timing_fadein" , cfg->osd->animation.timing_fadein );
+
+  bmp_cfg_db_set_int( cfgfile , "aosd" ,
+    "animation_timing_fadeout" , cfg->osd->animation.timing_fadeout );
+
+  /* text */
+  for ( i = 0 ; i < AOSD_TEXT_FONTS_NUM ; i++ )
+  {
+    gchar *color_str = NULL;
+    gchar *key_str = NULL;
+    key_str = g_strdup_printf( "text_fonts_name_%i" , i );
+    bmp_cfg_db_set_string( cfgfile , "aosd" ,
+      key_str , cfg->osd->text.fonts_name[i] );
+    g_free( key_str );
+    key_str = g_strdup_printf( "text_fonts_color_%i" , i );
+    aosd_cfg_util_color_to_str( cfg->osd->text.fonts_color[i] , &color_str );
+    bmp_cfg_db_set_string( cfgfile , "aosd" ,
+      key_str , color_str );
+    g_free( key_str );
+    g_free( color_str );
+    key_str = g_strdup_printf( "text_fonts_draw_shadow_%i" , i );
+    bmp_cfg_db_set_bool( cfgfile , "aosd" ,
+      key_str , cfg->osd->text.fonts_draw_shadow[i] );
+    g_free( key_str );
+    key_str = g_strdup_printf( "text_fonts_shadow_color_%i" , i );
+    aosd_cfg_util_color_to_str( cfg->osd->text.fonts_shadow_color[i] , &color_str );
+    bmp_cfg_db_set_string( cfgfile , "aosd" ,
+      key_str , color_str );
+    g_free( key_str );
+    g_free( color_str );
+  }
+
+  /* decoration */
+  bmp_cfg_db_set_int( cfgfile , "aosd" ,
+    "decoration_code" , cfg->osd->decoration.code );
+
+  bmp_cfg_db_set_string( cfgfile , "aosd" ,
+    "decoration_skin_file" , cfg->osd->decoration.skin_file );
+
+  /* decoration - colors */
+  max_numcol = aosd_deco_style_get_max_numcol();
+  for ( i = 0 ; i < max_numcol ; i++ )
+  {
+    gchar *key_str = NULL;
+    gchar *color_str = NULL;
+    aosd_color_t color = g_array_index( cfg->osd->decoration.colors , aosd_color_t , i );
+    key_str = g_strdup_printf( "decoration_color_%i" , i );
+    aosd_cfg_util_color_to_str( color , &color_str );
+    bmp_cfg_db_set_string( cfgfile , "aosd" ,
+      key_str , color_str );
+    g_free( key_str );
+    g_free( color_str );
+  }
+
+  bmp_cfg_db_close( cfgfile );
+
+  return 0;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/aosd/aosd_cfg.h	Mon Jan 29 06:40:04 2007 -0800
@@ -0,0 +1,125 @@
+/*
+*
+* Author: Giacomo Lozito <james@develia.org>, (C) 2005-2007
+*
+* This program is free software; you can redistribute it and/or modify it
+* under the terms of the GNU General Public License as published by the
+* Free Software Foundation; either version 2 of the License, or (at your
+* option) any later version.
+*
+* This program 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 this program; if not, write to the Free Software Foundation, Inc.,
+* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
+*
+*/
+
+#ifndef _I_AOSD_CFG_H
+#define _I_AOSD_CFG_H 1
+
+#include "aosd_common.h"
+#include <glib.h>
+
+/* in this release only one user font is supported */
+#define AOSD_TEXT_FONTS_NUM 1
+
+enum
+{
+  AOSD_POSITION_PLACEMENT_TOPLEFT = 1,
+  AOSD_POSITION_PLACEMENT_TOP,
+  AOSD_POSITION_PLACEMENT_TOPRIGHT,
+  AOSD_POSITION_PLACEMENT_MIDDLELEFT,
+  AOSD_POSITION_PLACEMENT_MIDDLE,
+  AOSD_POSITION_PLACEMENT_MIDDLERIGHT,
+  AOSD_POSITION_PLACEMENT_BOTTOMLEFT,
+  AOSD_POSITION_PLACEMENT_BOTTOM,
+  AOSD_POSITION_PLACEMENT_BOTTOMRIGHT
+};
+
+
+typedef struct
+{
+  guint16 red;
+  guint16 green;
+  guint16 blue;
+  guint16 alpha;
+}
+aosd_color_t;
+
+
+/* config portion containing osd decoration information */
+typedef struct
+{
+  gint code;
+  GArray *colors;
+  gchar *skin_file;
+}
+aosd_cfg_osd_decoration_t;
+
+
+/* config portion containing osd text information */
+typedef struct
+{
+  gchar *fonts_name[AOSD_TEXT_FONTS_NUM];
+  aosd_color_t fonts_color[AOSD_TEXT_FONTS_NUM];
+  gboolean fonts_draw_shadow[AOSD_TEXT_FONTS_NUM];
+  aosd_color_t fonts_shadow_color[AOSD_TEXT_FONTS_NUM];
+}
+aosd_cfg_osd_text_t;
+
+
+/* config portion containing osd animation information */
+typedef struct
+{
+  gint timing_display;
+  gint timing_fadein;
+  gint timing_fadeout;
+}
+aosd_cfg_osd_animation_t;
+
+
+/* config portion containing osd position information */
+typedef struct
+{
+  gint placement;
+  gint offset_x;
+  gint offset_y;
+}
+aosd_cfg_osd_position_t;
+
+
+/* config portion containing all information */
+typedef struct
+{
+  aosd_cfg_osd_position_t position;
+  aosd_cfg_osd_animation_t animation;
+  aosd_cfg_osd_text_t text;
+  aosd_cfg_osd_decoration_t decoration;
+}
+aosd_cfg_osd_t;
+
+
+/* config portion containing all config information */
+typedef struct
+{
+  gboolean set;
+
+  aosd_cfg_osd_t * osd;
+}
+aosd_cfg_t;
+
+
+/* API */
+aosd_cfg_t * aosd_cfg_new ( void );
+void aosd_cfg_delete ( aosd_cfg_t * cfg );
+aosd_cfg_osd_t * aosd_cfg_osd_new( void );
+void aosd_cfg_osd_delete ( aosd_cfg_osd_t * cfg_osd );
+aosd_cfg_osd_t * aosd_cfg_osd_copy ( aosd_cfg_osd_t * cfg_osd );
+gint aosd_cfg_load ( aosd_cfg_t * cfg );
+gint aosd_cfg_save ( aosd_cfg_t * cfg );
+
+#endif /* !_I_AOSD_CFG_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/aosd/aosd_common.h	Mon Jan 29 06:40:04 2007 -0800
@@ -0,0 +1,35 @@
+/*
+*
+* Author: Giacomo Lozito <james@develia.org>, (C) 2005-2007
+*
+* This program is free software; you can redistribute it and/or modify it
+* under the terms of the GNU General Public License as published by the
+* Free Software Foundation; either version 2 of the License, or (at your
+* option) any later version.
+*
+* This program 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 this program; if not, write to the Free Software Foundation, Inc.,
+* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
+*
+*/
+
+#ifndef _I_AOSD_COMMON_H
+#define _I_AOSD_COMMON_H 1
+
+#ifdef DEBUG
+#include <stdio.h>
+#define DEBUGMSG(...) { fprintf(stderr, "statusicon(%s:%s:%d): ", __FILE__, __FUNCTION__, (int) __LINE__); fprintf(stderr, __VA_ARGS__); }
+#else
+#define DEBUGMSG(...)
+#endif /* DEBUG */
+
+#include "../../config.h"
+
+#define AOSD_VERSION_PLUGIN "0.1"
+
+#endif /* !_I_AOSD_COMMON_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/aosd/aosd_osd.c	Mon Jan 29 06:40:04 2007 -0800
@@ -0,0 +1,344 @@
+/*
+*
+* Author: Giacomo Lozito <james@develia.org>, (C) 2005-2007
+*
+* This program is free software; you can redistribute it and/or modify it
+* under the terms of the GNU General Public License as published by the
+* Free Software Foundation; either version 2 of the License, or (at your
+* option) any later version.
+*
+* This program 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 this program; if not, write to the Free Software Foundation, Inc.,
+* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
+*
+*/
+
+#include "aosd_osd.h"
+#include "aosd_style.h"
+#include "aosd_style_private.h"
+#include "aosd_cfg.h"
+#include <glib.h>
+#include <X11/Xlib.h>
+#include <cairo/cairo.h>
+#include <pango/pangocairo.h>
+#include <gdk/gdk.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <sys/time.h>
+#include "ghosd.h"
+#include "ghosd-text.h"
+
+
+#define AOSD_STATUS_HIDDEN 0
+#define AOSD_STATUS_SHOWN  1
+#define AOSD_STATUS_UPDATE 2
+
+
+static pthread_t * aosd_thread = NULL;
+static pthread_mutex_t aosd_status_mutex = PTHREAD_MUTEX_INITIALIZER;
+static pthread_cond_t aosd_status_cond = PTHREAD_COND_INITIALIZER;
+static gboolean aosd_status = AOSD_STATUS_HIDDEN;
+
+
+typedef struct
+{
+  gchar * markup_message;
+  aosd_cfg_osd_t * cfg_osd;
+  gboolean cfg_is_copied;
+}
+aosd_thread_data_t;
+
+
+typedef struct
+{
+  cairo_surface_t * surface;
+  float alpha;
+  void * user_data;
+  gint width;
+  gint height;
+  gint deco_code;
+}
+GhosdFadeData;
+
+
+static void
+aosd_fade_func ( Ghosd * osd , cairo_t * cr , void * user_data )
+{
+  GhosdFadeData *fade_data = user_data;
+
+  if ( fade_data->surface == NULL )
+  {
+    cairo_t *rendered_cr;
+    fade_data->surface = cairo_surface_create_similar( cairo_get_target( cr ) ,
+                           CAIRO_CONTENT_COLOR_ALPHA , fade_data->width , fade_data->height );
+    rendered_cr = cairo_create( fade_data->surface );
+    aosd_deco_style_render( fade_data->deco_code , osd , rendered_cr , fade_data->user_data );
+    cairo_destroy( rendered_cr );
+  }
+
+  cairo_set_source_surface( cr , fade_data->surface , 0 , 0 );
+  cairo_paint_with_alpha( cr , fade_data->alpha );
+}
+
+
+static void *
+aosd_thread_func ( void * arg )
+{
+  aosd_thread_data_t *thread_data = (aosd_thread_data_t*)arg;
+  gchar *markup_string = thread_data->markup_message;
+  aosd_cfg_osd_t *cfg_osd = thread_data->cfg_osd;
+  Ghosd *osd;
+  GhosdFadeData fade_data = { NULL , 0 , NULL , 0 , 0 , 0 };
+  PangoContext *context;
+  PangoLayout *osd_layout;
+  gint max_width, layout_width, layout_height, pos_x = 0, pos_y = 0;
+  gint pad_left = 0 , pad_right = 0 , pad_top = 0 , pad_bottom = 0;
+  const gint screen_width = gdk_screen_get_width( gdk_screen_get_default() );
+  const gint screen_height = gdk_screen_get_height( gdk_screen_get_default() );
+  const gint STEP_MS = 50;
+  const gfloat dalpha_in = 1.0 / ( cfg_osd->animation.timing_fadein / (gfloat)STEP_MS );
+  const gfloat dalpha_out = 1.0 / ( cfg_osd->animation.timing_fadeout / (gfloat)STEP_MS );
+  struct timeval tv_nextupdate;
+  gboolean stop_now = FALSE;
+  aosd_deco_style_data_t style_data;
+
+  /* pick padding from selected decoration style */
+  aosd_deco_style_get_padding( cfg_osd->decoration.code ,
+    &pad_top , &pad_bottom , &pad_left , &pad_right );
+
+  max_width = screen_width - pad_left - pad_right - abs(cfg_osd->position.offset_x);
+  context = pango_cairo_font_map_create_context(
+              PANGO_CAIRO_FONT_MAP(pango_cairo_font_map_get_default()));
+  osd_layout = pango_layout_new(context);
+  pango_layout_set_markup( osd_layout, markup_string , -1 );
+  pango_layout_set_ellipsize( osd_layout , PANGO_ELLIPSIZE_NONE );
+  pango_layout_set_justify( osd_layout , FALSE );
+  pango_layout_set_width( osd_layout , PANGO_SCALE * max_width );
+  pango_layout_get_pixel_size( osd_layout , &layout_width , &layout_height );
+
+  osd = ghosd_new();
+
+  /* osd position */
+  switch ( cfg_osd->position.placement )
+  {
+    case AOSD_POSITION_PLACEMENT_TOP:
+      pos_x = (screen_width - (layout_width + pad_left + pad_right)) / 2;
+      pos_y = 0;
+      break;
+    case AOSD_POSITION_PLACEMENT_TOPRIGHT:
+      pos_x = screen_width - (layout_width + pad_left + pad_right);
+      pos_y = 0;
+      break;
+    case AOSD_POSITION_PLACEMENT_MIDDLELEFT:
+      pos_x = 0;
+      pos_y = (screen_height - (layout_height + pad_top + pad_bottom)) / 2;
+      break;
+    case AOSD_POSITION_PLACEMENT_MIDDLE:
+      pos_x = (screen_width - (layout_width + pad_left + pad_right)) / 2;
+      pos_y = (screen_height - (layout_height + pad_top + pad_bottom)) / 2;
+      break;
+    case AOSD_POSITION_PLACEMENT_MIDDLERIGHT:
+      pos_x = screen_width - (layout_width + pad_left + pad_right);
+      pos_y = (screen_height - (layout_height + pad_top + pad_bottom)) / 2;
+      break;
+    case AOSD_POSITION_PLACEMENT_BOTTOMLEFT:
+      pos_x = 0;
+      pos_y = screen_height - (layout_height + pad_top + pad_bottom);
+      break;
+    case AOSD_POSITION_PLACEMENT_BOTTOM:
+      pos_x = (screen_width - (layout_width + pad_left + pad_right)) / 2;
+      pos_y = screen_height - (layout_height + pad_top + pad_bottom);
+      break;
+    case AOSD_POSITION_PLACEMENT_BOTTOMRIGHT:
+      pos_x = screen_width - (layout_width + pad_left + pad_right);
+      pos_y = screen_height - (layout_height + pad_top + pad_bottom);
+      break;
+    case AOSD_POSITION_PLACEMENT_TOPLEFT:
+    default:
+      pos_x = 0;
+      pos_y = 0;
+      break;
+  }
+
+  /* add offset to position */
+  pos_x += cfg_osd->position.offset_x;
+  pos_y += cfg_osd->position.offset_y;
+
+  ghosd_set_position( osd , pos_x , pos_y ,
+    layout_width + pad_left + pad_right ,
+    layout_height + pad_top + pad_bottom );
+
+  /* the aosd_status must be checked during the fade and display process
+     (if another message arrives, the current transition must be abandoned ) */
+
+  style_data.layout = osd_layout;
+  style_data.text = &(cfg_osd->text);
+  style_data.decoration = &(cfg_osd->decoration);
+  fade_data.user_data = &style_data;
+  fade_data.width = layout_width + pad_left + pad_right;
+  fade_data.height = layout_height + pad_top + pad_bottom;
+  fade_data.alpha = 0;
+  fade_data.deco_code = cfg_osd->decoration.code;
+  ghosd_set_render( osd , (GhosdRenderFunc)aosd_fade_func , &fade_data , NULL );
+
+  /* show the osd (with alpha 0, invisible) */
+  ghosd_show( osd );
+
+  if ( stop_now != TRUE )
+  {
+    /* fade in */
+    for ( fade_data.alpha = 0 ; fade_data.alpha < 1.0 ; fade_data.alpha += dalpha_in )
+    {
+      pthread_mutex_lock( &aosd_status_mutex );
+      if ( aosd_status == AOSD_STATUS_UPDATE )
+        { pthread_mutex_unlock( &aosd_status_mutex ); stop_now = TRUE; break; }
+      pthread_mutex_unlock( &aosd_status_mutex );
+
+      if (fade_data.alpha > 1.0) fade_data.alpha = 1.0;
+      ghosd_render( osd );
+      gettimeofday( &tv_nextupdate , NULL );
+      tv_nextupdate.tv_usec += STEP_MS*1000;
+      ghosd_main_until( osd , &tv_nextupdate );
+    }
+  }
+
+  if ( stop_now != TRUE )
+  {
+    /* show the osd for the desidered amount of time */
+    gint time_iter_ms = 0;
+
+    fade_data.alpha = 1.0;
+    ghosd_render( osd );
+
+    while ( time_iter_ms < cfg_osd->animation.timing_display )
+    {
+      pthread_mutex_lock( &aosd_status_mutex );
+      if ( aosd_status == AOSD_STATUS_UPDATE )
+        { pthread_mutex_unlock( &aosd_status_mutex ); stop_now = TRUE; break; }
+      pthread_mutex_unlock( &aosd_status_mutex );
+
+      gettimeofday(&tv_nextupdate, NULL);
+      tv_nextupdate.tv_usec += STEP_MS*1000;
+      time_iter_ms += STEP_MS;
+      ghosd_main_until( osd , &tv_nextupdate);
+    }
+  }
+
+  if ( stop_now != TRUE )
+  {
+    /* fade out */
+    for ( fade_data.alpha = 1 ; fade_data.alpha > 0.0 ; fade_data.alpha -= dalpha_out )
+    {
+      pthread_mutex_lock( &aosd_status_mutex );
+      if ( aosd_status == AOSD_STATUS_UPDATE )
+        { pthread_mutex_unlock( &aosd_status_mutex ); stop_now = TRUE; break; }
+      pthread_mutex_unlock( &aosd_status_mutex );
+
+      if (fade_data.alpha < 0.0) fade_data.alpha = 0;
+      ghosd_render( osd );
+      gettimeofday( &tv_nextupdate , NULL );
+      tv_nextupdate.tv_usec += STEP_MS*1000;
+      ghosd_main_until( osd , &tv_nextupdate );
+    }
+  }
+
+  fade_data.alpha = 0;
+  ghosd_render( osd );
+
+  ghosd_hide( osd );
+  ghosd_main_iterations( osd );
+  ghosd_destroy( osd );
+  if ( fade_data.surface != NULL )
+    cairo_surface_destroy( fade_data.surface );
+
+  pthread_mutex_lock( &aosd_status_mutex );
+  if ( aosd_status == AOSD_STATUS_UPDATE )
+  {
+    aosd_status = AOSD_STATUS_SHOWN;
+    pthread_mutex_unlock( &aosd_status_mutex );
+    pthread_cond_signal( &aosd_status_cond );
+  }
+  else
+  {
+    aosd_status = AOSD_STATUS_HIDDEN;
+    pthread_mutex_unlock( &aosd_status_mutex );
+  }
+
+  g_free( markup_string );
+  if ( thread_data->cfg_is_copied == TRUE )
+    aosd_cfg_osd_delete( cfg_osd );
+  g_free( thread_data );
+  g_object_unref( osd_layout );
+  g_object_unref( context );
+  pthread_exit(NULL);
+}
+
+
+gint
+aosd_display ( gchar * markup_string , aosd_cfg_osd_t * cfg_osd , gboolean copy_cfg )
+{
+  aosd_thread_data_t *thread_data = g_malloc(sizeof(aosd_thread_data_t));
+  thread_data->markup_message = g_strdup( markup_string );
+  if ( copy_cfg == TRUE )
+  {
+    thread_data->cfg_osd = aosd_cfg_osd_copy( cfg_osd );
+    thread_data->cfg_is_copied = TRUE;
+  }
+  else
+  {
+    thread_data->cfg_osd = cfg_osd;
+    thread_data->cfg_is_copied = FALSE;
+  }
+
+  /* check if osd is already displaying a message now */
+  pthread_mutex_lock( &aosd_status_mutex );
+  if ( aosd_status == AOSD_STATUS_SHOWN )
+  {
+    /* a message is already being shown in osd, stop the
+       display of that message cause there is a new one */
+    aosd_status = AOSD_STATUS_UPDATE;
+    pthread_cond_wait( &aosd_status_cond , &aosd_status_mutex );
+  }
+  else
+  {
+    /* no message is being shown in osd, show one now */
+    aosd_status = AOSD_STATUS_SHOWN;
+  }
+  pthread_mutex_unlock( &aosd_status_mutex );
+
+  if ( aosd_thread != NULL )
+    pthread_join( *aosd_thread , NULL );
+  else
+    aosd_thread = g_malloc(sizeof(pthread_t));
+
+  pthread_create( aosd_thread , NULL , aosd_thread_func , thread_data );
+  return 0;
+}
+
+
+void
+aosd_shutdown ( void )
+{
+  if ( aosd_thread != NULL )
+  {
+    pthread_mutex_lock( &aosd_status_mutex );
+    if ( aosd_status == AOSD_STATUS_SHOWN )
+    {
+      /* a message is being shown in osd,
+         stop the display of that message */
+      aosd_status = AOSD_STATUS_UPDATE;
+      pthread_cond_wait( &aosd_status_cond , &aosd_status_mutex );
+    }
+    pthread_mutex_unlock( &aosd_status_mutex );
+    pthread_join( *aosd_thread , NULL );
+    g_free( aosd_thread );
+    aosd_thread = NULL;
+    aosd_status = AOSD_STATUS_HIDDEN; /* aosd_thread joined, no need to mutex this */
+  }
+  return;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/aosd/aosd_osd.h	Mon Jan 29 06:40:04 2007 -0800
@@ -0,0 +1,32 @@
+/*
+*
+* Author: Giacomo Lozito <james@develia.org>, (C) 2005-2007
+*
+* This program is free software; you can redistribute it and/or modify it
+* under the terms of the GNU General Public License as published by the
+* Free Software Foundation; either version 2 of the License, or (at your
+* option) any later version.
+*
+* This program 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 this program; if not, write to the Free Software Foundation, Inc.,
+* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
+*
+*/
+
+#ifndef _I_AOSD_OSD_H
+#define _I_AOSD_OSD_H 1
+
+#include "aosd_common.h"
+#include "aosd_cfg.h"
+#include <glib.h>
+
+
+gint aosd_display ( gchar * markup_string , aosd_cfg_osd_t * cfg_osd , gboolean copy_cfg );
+void aosd_shutdown ( void );
+
+#endif /* !_I_AOSD_OSD_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/aosd/aosd_style.c	Mon Jan 29 06:40:04 2007 -0800
@@ -0,0 +1,332 @@
+/*
+*
+* Author: Giacomo Lozito <james@develia.org>, (C) 2005-2007
+*
+* This program is free software; you can redistribute it and/or modify it
+* under the terms of the GNU General Public License as published by the
+* Free Software Foundation; either version 2 of the License, or (at your
+* option) any later version.
+*
+* This program 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 this program; if not, write to the Free Software Foundation, Inc.,
+* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
+*
+*/
+
+#include "aosd_style.h"
+#include "aosd_style_private.h"
+#include "aosd_cfg.h"
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <X11/Xlib.h>
+#include <cairo/cairo.h>
+#include <pango/pangocairo.h>
+#include "ghosd.h"
+#include "ghosd-text.h"
+
+
+/* HOW TO ADD A NEW DECORATION STYLE
+   --------------------------------------------------------------------------
+   First, add a new decoration style code; then, provide the decoration style
+   information by adding a new entry in the aosd_deco_styles[] array (name,
+   render function, etc.); add the new decoration style code in the array
+   of decoration style codes, and update the define containing the array size.
+   The render function uses three parameters; the Ghosd instance, the cairo
+   object you use to draw, and a aosd_deco_style_data_t object that contains
+   user-defined options (look into aosd_style.h and aosd_cfg.h for details).
+   Have fun! :)
+*/
+
+
+/* decoration style codes ( the code values do not need to be sequential ) */
+enum
+{
+  AOSD_DECO_STYLE_RECT = 0,
+  AOSD_DECO_STYLE_ROUNDRECT = 1,
+  AOSD_DECO_STYLE_CONCAVERECT = 2
+};
+
+/* decoration style codes array size */
+#define AOSD_DECO_STYLE_CODES_ARRAY_SIZE 3
+
+/* decoration style codes array */
+gint aosd_deco_style_codes[] =
+{
+  AOSD_DECO_STYLE_RECT,
+  AOSD_DECO_STYLE_ROUNDRECT,
+  AOSD_DECO_STYLE_CONCAVERECT
+};
+
+/* prototypes of render functions */
+static void aosd_deco_rfunc_rect ( Ghosd * , cairo_t * , aosd_deco_style_data_t * );
+static void aosd_deco_rfunc_roundrect ( Ghosd * , cairo_t * , aosd_deco_style_data_t * );
+static void aosd_deco_rfunc_concaverect ( Ghosd * , cairo_t * , aosd_deco_style_data_t * );
+
+/* map decoration style codes to decoration objects */
+aosd_deco_style_t aosd_deco_styles[] =
+{
+  [AOSD_DECO_STYLE_RECT] = { N_("Rectangle") ,
+                             aosd_deco_rfunc_rect ,
+                             2 , { 10 , 10 , 10 , 10 } },
+
+  [AOSD_DECO_STYLE_ROUNDRECT] = { N_("Rounded Rectangle") ,
+                                  aosd_deco_rfunc_roundrect ,
+                                  2 , { 10 , 10 , 10 , 10 } },
+
+  [AOSD_DECO_STYLE_CONCAVERECT] = { N_("Concave Rectangle") ,
+                                    aosd_deco_rfunc_concaverect ,
+                                    2 , { 10 , 10 , 10 , 10 } }
+};
+
+
+
+/* DECORATION STYLE API */
+
+
+void
+aosd_deco_style_get_codes_array ( gint ** array , gint * array_size )
+{
+  *array = aosd_deco_style_codes;
+  *array_size = AOSD_DECO_STYLE_CODES_ARRAY_SIZE;
+  return;
+}
+
+
+void
+aosd_deco_style_get_padding ( gint deco_code ,
+                              gint * ptop , gint * pbottom ,
+                              gint * pleft , gint * pright )
+{
+  if ( ptop != NULL ) *ptop = aosd_deco_styles[deco_code].padding.top;
+  if ( pbottom != NULL ) *pbottom = aosd_deco_styles[deco_code].padding.bottom;
+  if ( pleft != NULL ) *pleft = aosd_deco_styles[deco_code].padding.left;
+  if ( pright != NULL ) *pright = aosd_deco_styles[deco_code].padding.right;
+  return;
+}
+
+
+const gchar *
+aosd_deco_style_get_desc ( gint deco_code )
+{
+  return aosd_deco_styles[deco_code].desc;
+}
+
+
+gint
+aosd_deco_style_get_numcol ( gint deco_code )
+{
+  return aosd_deco_styles[deco_code].colors_num;
+}
+
+
+void
+aosd_deco_style_render ( gint deco_code , gpointer ghosd , gpointer cr , gpointer user_data )
+{
+  aosd_deco_styles[deco_code].render_func( (Ghosd*)ghosd , (cairo_t*)cr , user_data );
+}
+
+
+gint
+aosd_deco_style_get_first_code ( void )
+{
+  return AOSD_DECO_STYLE_RECT;
+}
+
+
+gint
+aosd_deco_style_get_max_numcol ( void )
+{
+  gint i = 0;
+  gint max_numcol = 0;
+
+  for ( i = 0 ; i < AOSD_DECO_STYLE_CODES_ARRAY_SIZE ; i++ )
+  {
+    gint numcol = aosd_deco_style_get_numcol( aosd_deco_style_codes[i] );
+    if ( numcol > max_numcol )
+      max_numcol = numcol;
+  }
+
+  return max_numcol;
+}
+
+
+
+/* RENDER FUNCTIONS */
+
+static void
+aosd_deco_rfunc_rect( Ghosd * osd , cairo_t * cr , aosd_deco_style_data_t * data )
+{
+  /* decoration information
+     ----------------------
+     draws a simple rectangular decoration; uses 2 colors
+     (user color 1 and 2) and 1 font (user font 1), with optional shadow
+  */
+  PangoLayout *osd_layout = data->layout;
+  aosd_color_t color0 = g_array_index( data->decoration->colors , aosd_color_t , 0 );
+  aosd_color_t color1 = g_array_index( data->decoration->colors , aosd_color_t , 1 );
+  aosd_color_t textcolor0 = data->text->fonts_color[0];
+  aosd_color_t shadowcolor0 = data->text->fonts_shadow_color[0];
+  gboolean draw_shadow = data->text->fonts_draw_shadow[0];
+  gint width = 0, height = 0;
+
+  pango_layout_get_pixel_size( osd_layout , &width , &height );
+
+  /* draw rectangle container */
+  cairo_set_source_rgba( cr , (gdouble)color0.red / 65535 , (gdouble)color0.green / 65535 ,
+    (gdouble)color0.blue / 65535 , (gdouble)color0.alpha / 65535 );
+  cairo_rectangle( cr , 0 , 0 ,
+    aosd_deco_styles[AOSD_DECO_STYLE_RECT].padding.left + width +
+    aosd_deco_styles[AOSD_DECO_STYLE_RECT].padding.right,
+    aosd_deco_styles[AOSD_DECO_STYLE_RECT].padding.top + height +
+    aosd_deco_styles[AOSD_DECO_STYLE_RECT].padding.bottom );
+  cairo_fill_preserve( cr );
+  cairo_set_source_rgba( cr , (gdouble)color1.red / 65535 , (gdouble)color1.green / 65535 ,
+    (gdouble)color1.blue / 65535 , (gdouble)color1.alpha / 65535 );
+  cairo_stroke( cr );
+
+  if ( draw_shadow == TRUE )
+  {
+    /* draw text shadow */
+    cairo_set_source_rgba( cr , (gdouble)shadowcolor0.red / 65535 , (gdouble)shadowcolor0.green / 65535 ,
+      (gdouble)shadowcolor0.blue / 65535 , (gdouble)shadowcolor0.alpha / 65535 );
+    cairo_move_to( cr,
+      aosd_deco_styles[AOSD_DECO_STYLE_RECT].padding.left + 2 ,
+      aosd_deco_styles[AOSD_DECO_STYLE_RECT].padding.top + 2 );
+    pango_cairo_show_layout( cr , osd_layout );
+  }
+
+  /* draw text */
+  cairo_set_source_rgba( cr , (gdouble)textcolor0.red / 65535 , (gdouble)textcolor0.green / 65535 ,
+    (gdouble)textcolor0.blue / 65535 , (gdouble)textcolor0.alpha / 65535 );
+  cairo_move_to( cr,
+    aosd_deco_styles[AOSD_DECO_STYLE_RECT].padding.left ,
+    aosd_deco_styles[AOSD_DECO_STYLE_RECT].padding.top );
+  pango_cairo_show_layout( cr , osd_layout );
+}
+
+
+static void
+aosd_deco_rfunc_roundrect ( Ghosd * osd , cairo_t * cr , aosd_deco_style_data_t * data )
+{
+  /* decoration information
+     ----------------------
+     draws a rectangular decoration with rounded angles; uses 2 colors
+     (user color 1 and 2) and 1 font (user font 1), with optional shadow
+  */
+  PangoLayout *osd_layout = data->layout;
+  aosd_color_t color0 = g_array_index( data->decoration->colors , aosd_color_t , 0 );
+  aosd_color_t color1 = g_array_index( data->decoration->colors , aosd_color_t , 1 );
+  aosd_color_t textcolor0 = data->text->fonts_color[0];
+  aosd_color_t shadowcolor0 = data->text->fonts_shadow_color[0];
+  gboolean draw_shadow = data->text->fonts_draw_shadow[0];
+  gint width = 0, height = 0;
+
+  pango_layout_get_pixel_size( osd_layout , &width , &height );
+
+  /* draw rounded-rectangle container */
+  cairo_set_source_rgba( cr , (gdouble)color0.red / 65535 , (gdouble)color0.green / 65535 ,
+    (gdouble)color0.blue / 65535 , (gdouble)color0.alpha / 65535 );
+  cairo_move_to( cr , aosd_deco_styles[AOSD_DECO_STYLE_ROUNDRECT].padding.left , 0 );
+  cairo_arc( cr , width + aosd_deco_styles[AOSD_DECO_STYLE_ROUNDRECT].padding.left ,
+    aosd_deco_styles[AOSD_DECO_STYLE_ROUNDRECT].padding.top ,
+    10. , -G_PI_2 , 0. );
+  cairo_arc( cr , width + aosd_deco_styles[AOSD_DECO_STYLE_ROUNDRECT].padding.left ,
+    aosd_deco_styles[AOSD_DECO_STYLE_ROUNDRECT].padding.top + height ,
+    10. , -4. * G_PI_2 , -3. * G_PI_2 );
+  cairo_arc( cr , aosd_deco_styles[AOSD_DECO_STYLE_ROUNDRECT].padding.left ,
+    aosd_deco_styles[AOSD_DECO_STYLE_ROUNDRECT].padding.top + height ,
+    10. , -3. * G_PI_2 , -2. * G_PI_2 );
+  cairo_arc( cr , aosd_deco_styles[AOSD_DECO_STYLE_ROUNDRECT].padding.left ,
+    aosd_deco_styles[AOSD_DECO_STYLE_ROUNDRECT].padding.top ,
+    10. , -2. * G_PI_2 , -G_PI_2 );
+  cairo_close_path( cr );
+  cairo_fill_preserve( cr );
+  cairo_set_source_rgba( cr , (gdouble)color1.red / 65535 , (gdouble)color1.green / 65535 ,
+    (gdouble)color1.blue / 65535 , (gdouble)color1.alpha / 65535 );
+  cairo_stroke( cr );
+
+  if ( draw_shadow == TRUE )
+  {
+    /* draw text shadow */
+    cairo_set_source_rgba( cr , (gdouble)shadowcolor0.red / 65535 , (gdouble)shadowcolor0.green / 65535 ,
+      (gdouble)shadowcolor0.blue / 65535 , (gdouble)shadowcolor0.alpha / 65535 );
+    cairo_move_to( cr ,
+      aosd_deco_styles[AOSD_DECO_STYLE_ROUNDRECT].padding.left + 2 ,
+      aosd_deco_styles[AOSD_DECO_STYLE_ROUNDRECT].padding.top + 2 );
+    pango_cairo_show_layout( cr , osd_layout );
+  }
+
+  /* draw text */
+  cairo_set_source_rgba( cr , (gdouble)textcolor0.red / 65535 , (gdouble)textcolor0.green / 65535 ,
+    (gdouble)textcolor0.blue / 65535 , (gdouble)textcolor0.alpha / 65535 );
+  cairo_move_to( cr ,
+    aosd_deco_styles[AOSD_DECO_STYLE_ROUNDRECT].padding.left ,
+    aosd_deco_styles[AOSD_DECO_STYLE_ROUNDRECT].padding.top );
+  pango_cairo_show_layout( cr , osd_layout );
+}
+
+
+static void
+aosd_deco_rfunc_concaverect ( Ghosd * osd , cairo_t * cr , aosd_deco_style_data_t * data )
+{
+  /* decoration information
+     ----------------------
+     draws a rectangle with concave angles; uses 2 colors
+     (user color 1 and 2) and 1 font (user font 1), with optional shadow
+  */
+  PangoLayout *osd_layout = data->layout;
+  aosd_color_t color0 = g_array_index( data->decoration->colors , aosd_color_t , 0 );
+  aosd_color_t color1 = g_array_index( data->decoration->colors , aosd_color_t , 1 );
+  aosd_color_t textcolor0 = data->text->fonts_color[0];
+  aosd_color_t shadowcolor0 = data->text->fonts_shadow_color[0];
+  gboolean draw_shadow = data->text->fonts_draw_shadow[0];
+  gint width = 0, height = 0;
+
+  pango_layout_get_pixel_size( osd_layout , &width , &height );
+
+  /* draw jigsaw-piece-like container */
+  cairo_set_source_rgba( cr , (gdouble)color0.red / 65535 , (gdouble)color0.green / 65535 ,
+    (gdouble)color0.blue / 65535 , (gdouble)color0.alpha / 65535 );
+  cairo_move_to( cr , aosd_deco_styles[AOSD_DECO_STYLE_CONCAVERECT].padding.left , 0 );
+  cairo_arc_negative( cr , width + aosd_deco_styles[AOSD_DECO_STYLE_CONCAVERECT].padding.left + 2 ,
+    aosd_deco_styles[AOSD_DECO_STYLE_CONCAVERECT].padding.top - 2 ,
+    8. , -G_PI_2 , 0. );
+  cairo_arc_negative( cr , width + aosd_deco_styles[AOSD_DECO_STYLE_CONCAVERECT].padding.left + 2 ,
+    aosd_deco_styles[AOSD_DECO_STYLE_CONCAVERECT].padding.top + height + 2 ,
+    8. , -4. * G_PI_2 , -3. * G_PI_2 );
+  cairo_arc_negative( cr , aosd_deco_styles[AOSD_DECO_STYLE_CONCAVERECT].padding.left - 2 ,
+    aosd_deco_styles[AOSD_DECO_STYLE_CONCAVERECT].padding.top + height + 2 ,
+    8. , -3. * G_PI_2 , -2. * G_PI_2 );
+  cairo_arc_negative( cr , aosd_deco_styles[AOSD_DECO_STYLE_CONCAVERECT].padding.left - 2 ,
+    aosd_deco_styles[AOSD_DECO_STYLE_CONCAVERECT].padding.top - 2 ,
+    8. , -2. * G_PI_2 , -G_PI_2 );
+  cairo_close_path( cr );
+  cairo_fill_preserve( cr );
+  cairo_set_source_rgba( cr , (gdouble)color1.red / 65535 , (gdouble)color1.green / 65535 ,
+    (gdouble)color1.blue / 65535 , (gdouble)color1.alpha / 65535 );
+  cairo_stroke( cr );
+
+  if ( draw_shadow == TRUE )
+  {
+    /* draw text shadow */
+    cairo_set_source_rgba( cr , (gdouble)shadowcolor0.red / 65535 , (gdouble)shadowcolor0.green / 65535 ,
+      (gdouble)shadowcolor0.blue / 65535 , (gdouble)shadowcolor0.alpha / 65535 );
+    cairo_move_to( cr ,
+      aosd_deco_styles[AOSD_DECO_STYLE_CONCAVERECT].padding.left + 2 ,
+      aosd_deco_styles[AOSD_DECO_STYLE_CONCAVERECT].padding.top + 2 );
+    pango_cairo_show_layout( cr , osd_layout );
+  }
+
+  /* draw text */
+  cairo_set_source_rgba( cr , (gdouble)textcolor0.red / 65535 , (gdouble)textcolor0.green / 65535 ,
+    (gdouble)textcolor0.blue / 65535 , (gdouble)textcolor0.alpha / 65535 );
+  cairo_move_to( cr ,
+    aosd_deco_styles[AOSD_DECO_STYLE_CONCAVERECT].padding.left ,
+    aosd_deco_styles[AOSD_DECO_STYLE_CONCAVERECT].padding.top );
+  pango_cairo_show_layout( cr , osd_layout );
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/aosd/aosd_style.h	Mon Jan 29 06:40:04 2007 -0800
@@ -0,0 +1,39 @@
+/*
+*
+* Author: Giacomo Lozito <james@develia.org>, (C) 2005-2007
+*
+* This program is free software; you can redistribute it and/or modify it
+* under the terms of the GNU General Public License as published by the
+* Free Software Foundation; either version 2 of the License, or (at your
+* option) any later version.
+*
+* This program 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 this program; if not, write to the Free Software Foundation, Inc.,
+* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
+*
+*/
+
+#ifndef _I_AOSD_STYLE_H
+#define _I_AOSD_STYLE_H 1
+
+#include "aosd_common.h"
+#include <glib.h>
+
+
+/* decoration style public API */
+void aosd_deco_style_get_codes_array ( gint ** , gint * );
+void aosd_deco_style_get_padding ( gint , gint * , gint * , gint * , gint * );
+const gchar * aosd_deco_style_get_desc ( gint );
+gint aosd_deco_style_get_numcol ( gint );
+void aosd_deco_style_render ( gint , gpointer , gpointer , gpointer ); /* opaque */
+
+gint aosd_deco_style_get_first_code ( void );
+gint aosd_deco_style_get_max_numcol ( void );
+
+
+#endif /* !_I_AOSD_STYLE_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/aosd/aosd_style_private.h	Mon Jan 29 06:40:04 2007 -0800
@@ -0,0 +1,71 @@
+/*
+*
+* Author: Giacomo Lozito <james@develia.org>, (C) 2005-2007
+*
+* This program is free software; you can redistribute it and/or modify it
+* under the terms of the GNU General Public License as published by the
+* Free Software Foundation; either version 2 of the License, or (at your
+* option) any later version.
+*
+* This program 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 this program; if not, write to the Free Software Foundation, Inc.,
+* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
+*
+*/
+
+#ifndef _I_AOSD_STYLE_PRIVATE_H
+#define _I_AOSD_STYLE_PRIVATE_H 1
+
+#include "aosd_common.h"
+#include "aosd_cfg.h"
+#include <glib.h>
+#include <cairo/cairo.h>
+#include <pango/pangocairo.h>
+#include "ghosd.h"
+
+
+/* decoration render function parameter object
+   ----------------------------------------------------------
+   layout         pango layout that contains OSD text
+   text           user-defined options for OSD text
+   decoration     user-defined options for OSD decorations
+*/
+typedef struct
+{
+  PangoLayout * layout;
+  aosd_cfg_osd_text_t * text;
+  aosd_cfg_osd_decoration_t * decoration;
+}
+aosd_deco_style_data_t;
+
+
+/* decoration object
+   ----------------------------------------------------------
+   desc           description
+   render_func    render function used to draw the decoration
+   colors_num     number of user-definable colors
+   padding        drawable space available around the text
+*/
+typedef struct
+{
+  const gchar * desc;
+  void (*render_func)( Ghosd * , cairo_t * , aosd_deco_style_data_t * );
+  gint colors_num;
+  struct
+  {
+    gint top;
+    gint bottom;
+    gint left;
+    gint right;
+  }
+  padding;
+}
+aosd_deco_style_t;
+
+
+#endif /* !_I_AOSD_STYLE_PRIVATE_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/aosd/aosd_ui.c	Mon Jan 29 06:40:04 2007 -0800
@@ -0,0 +1,867 @@
+/*
+*
+* Author: Giacomo Lozito <james@develia.org>, (C) 2005-2007
+*
+* This program is free software; you can redistribute it and/or modify it
+* under the terms of the GNU General Public License as published by the
+* Free Software Foundation; either version 2 of the License, or (at your
+* option) any later version.
+*
+* This program 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 this program; if not, write to the Free Software Foundation, Inc.,
+* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
+*
+*/
+
+#include "aosd_ui.h"
+#include "aosd_style.h"
+#include "aosd_cfg.h"
+#include "aosd_osd.h"
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <gdk/gdk.h>
+#include <gtk/gtk.h>
+
+
+extern aosd_cfg_t * global_config;
+
+
+/*************************************************************/
+/* small callback system used by the configuration interface */
+typedef void (*aosd_ui_cb_func_t)( GtkWidget * , aosd_cfg_t * );
+
+typedef struct
+{
+  aosd_ui_cb_func_t func;
+  GtkWidget * widget;
+}
+aosd_ui_cb_t;
+
+static void
+aosd_callback_list_add ( GList ** list , GtkWidget * widget , aosd_ui_cb_func_t func )
+{
+  aosd_ui_cb_t *cb = g_malloc(sizeof(aosd_ui_cb_t));
+  cb->widget = widget;
+  cb->func = func;
+  *list = g_list_append( *list , cb );
+  return;
+}
+
+static void
+aosd_callback_list_run ( GList * list , aosd_cfg_t * cfg )
+{
+  while ( list != NULL )
+  {
+    aosd_ui_cb_t *cb = (aosd_ui_cb_t*)list->data;
+    cb->func( cb->widget , cfg );
+    list = g_list_next( list );
+  }
+  return;
+}
+
+static void
+aosd_callback_list_free ( GList * list )
+{
+  GList *list_top = list;
+  while ( list != NULL )
+  {
+    g_free( (aosd_ui_cb_t*)list->data );
+    list = g_list_next( list );
+  }
+  g_list_free( list_top );
+  return;
+}
+/*************************************************************/
+
+
+
+static gboolean
+aosd_cb_configure_position_expose ( GtkWidget * darea ,
+                                    GdkEventExpose * event ,
+                                    gpointer coord_gp )
+{
+  gint coord = GPOINTER_TO_INT(coord_gp);
+  gdk_draw_rectangle( GDK_DRAWABLE(darea->window) ,
+                      darea->style->black_gc , TRUE ,
+                      (coord % 3) * 10 , (coord / 3) * 16 , 20 , 8 );
+  return FALSE;
+}
+
+
+static void
+aosd_cb_configure_position_placement_commit ( GtkWidget * table , aosd_cfg_t * cfg )
+{
+  GList *placbt_list = gtk_container_get_children( GTK_CONTAINER(table) );
+  GList *list_iter = placbt_list;
+
+  while ( list_iter != NULL )
+  {
+    GtkWidget *placbt = list_iter->data;
+    if ( gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON(placbt) ) == TRUE )
+    {
+      cfg->osd->position.placement = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(placbt),"value"));
+      break;
+    }
+    list_iter = g_list_next( list_iter );
+  }
+
+  g_list_free( placbt_list );
+  return;
+}
+
+
+static void
+aosd_cb_configure_position_offset_commit ( GtkWidget * table , aosd_cfg_t * cfg )
+{
+  cfg->osd->position.offset_x = gtk_spin_button_get_value_as_int(
+    GTK_SPIN_BUTTON(g_object_get_data(G_OBJECT(table),"offx")) );
+  cfg->osd->position.offset_y = gtk_spin_button_get_value_as_int(
+    GTK_SPIN_BUTTON(g_object_get_data(G_OBJECT(table),"offy")) );
+  return;
+}
+
+
+static GtkWidget *
+aosd_ui_configure_position ( aosd_cfg_t * cfg , GList ** cb_list )
+{
+  GtkWidget *pos_vbox;
+  GtkWidget *pos_placement_frame, *pos_placement_hbox, *pos_placement_table;
+  GtkWidget *pos_placement_bt[9], *pos_placement_bt_darea[9];
+  GtkWidget *pos_offset_table, *pos_offset_x_label, *pos_offset_x_spinbt;
+  GtkWidget *pos_offset_y_label, *pos_offset_y_spinbt;
+  gint i = 0;
+
+  pos_vbox = gtk_vbox_new( FALSE , 0 );
+  gtk_container_set_border_width( GTK_CONTAINER(pos_vbox) , 6 );
+
+  pos_placement_frame = gtk_frame_new( _("Placement") );
+  pos_placement_hbox = gtk_hbox_new( FALSE , 0 );
+  gtk_container_set_border_width( GTK_CONTAINER(pos_placement_hbox) , 6 );
+  gtk_container_add( GTK_CONTAINER(pos_placement_frame) , pos_placement_hbox );
+  gtk_box_pack_start( GTK_BOX(pos_vbox) , pos_placement_frame , FALSE , FALSE , 0 );
+
+  pos_placement_table = gtk_table_new( 3 , 3 , TRUE );
+  for ( i = 0 ; i < 9 ; i++ )
+  {
+    if ( i == 0 )
+      pos_placement_bt[i] = gtk_radio_button_new( NULL );
+    else
+      pos_placement_bt[i] = gtk_radio_button_new_from_widget( GTK_RADIO_BUTTON(pos_placement_bt[0]) );
+    gtk_toggle_button_set_mode( GTK_TOGGLE_BUTTON(pos_placement_bt[i]) , FALSE );
+    pos_placement_bt_darea[i] = gtk_drawing_area_new();
+    gtk_widget_set_size_request( pos_placement_bt_darea[i] , 40 , 40 );
+    gtk_container_add( GTK_CONTAINER(pos_placement_bt[i]) , pos_placement_bt_darea[i] );
+    g_signal_connect( G_OBJECT(pos_placement_bt_darea[i]) , "expose-event" ,
+                      G_CALLBACK(aosd_cb_configure_position_expose) , GINT_TO_POINTER(i) );
+    gtk_table_attach( GTK_TABLE(pos_placement_table) , pos_placement_bt[i] ,
+                      (i % 3) , (i % 3) + 1 , (i / 3) , (i / 3) + 1 ,
+                      GTK_FILL , GTK_FILL , 0 , 0 );
+    g_object_set_data( G_OBJECT(pos_placement_bt[i]) , "value" , GINT_TO_POINTER(i+1) );
+    if ( cfg->osd->position.placement == (i+1) )
+      gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON(pos_placement_bt[i]) , TRUE );
+  }
+  gtk_box_pack_start( GTK_BOX(pos_placement_hbox) , pos_placement_table , FALSE , FALSE , 0 );
+  aosd_callback_list_add( cb_list , pos_placement_table , aosd_cb_configure_position_placement_commit );
+
+  gtk_box_pack_start( GTK_BOX(pos_placement_hbox) , gtk_vseparator_new() , FALSE , FALSE , 6 );
+
+  pos_offset_table = gtk_table_new( 2 , 2 , FALSE );
+  gtk_table_set_row_spacings( GTK_TABLE(pos_offset_table) , 4 );
+  gtk_table_set_col_spacings( GTK_TABLE(pos_offset_table) , 4 );
+  pos_offset_x_label = gtk_label_new( _( "Relative X offset:" ) );
+  gtk_table_attach( GTK_TABLE(pos_offset_table) , pos_offset_x_label ,
+                    0 , 1 , 0 , 1 , GTK_FILL , GTK_FILL , 0 , 0 );
+  pos_offset_x_spinbt = gtk_spin_button_new_with_range( -9999 , 9999 , 1 );
+  gtk_spin_button_set_value( GTK_SPIN_BUTTON(pos_offset_x_spinbt) , cfg->osd->position.offset_x );
+  gtk_table_attach( GTK_TABLE(pos_offset_table) , pos_offset_x_spinbt ,
+                    1 , 2 , 0 , 1 , GTK_FILL , GTK_FILL , 0 , 0 );
+  g_object_set_data( G_OBJECT(pos_offset_table) , "offx" , pos_offset_x_spinbt );
+  pos_offset_y_label = gtk_label_new( _( "Relative Y offset:" ) );
+  gtk_table_attach( GTK_TABLE(pos_offset_table) , pos_offset_y_label ,
+                    0 , 1 , 1 , 2 , GTK_FILL , GTK_FILL , 0 , 0 );
+  pos_offset_y_spinbt = gtk_spin_button_new_with_range( -9999 , 9999 , 1 );
+  gtk_spin_button_set_value( GTK_SPIN_BUTTON(pos_offset_y_spinbt) , cfg->osd->position.offset_y );
+  gtk_table_attach( GTK_TABLE(pos_offset_table) , pos_offset_y_spinbt ,
+                    1 , 2 , 1 , 2 , GTK_FILL , GTK_FILL , 0 , 0 );
+  g_object_set_data( G_OBJECT(pos_offset_table) , "offy" , pos_offset_y_spinbt );
+  gtk_box_pack_start( GTK_BOX(pos_placement_hbox) , pos_offset_table , FALSE , FALSE , 0 );
+  aosd_callback_list_add( cb_list , pos_offset_table , aosd_cb_configure_position_offset_commit );
+
+  return pos_vbox;
+}
+
+
+static GtkWidget *
+aosd_ui_configure_animation_timing ( gchar * label_string )
+{
+  GtkWidget *hbox, *desc_label, *spinbt;
+  hbox = gtk_hbox_new( FALSE , 4 );
+  desc_label = gtk_label_new( label_string );
+  spinbt = gtk_spin_button_new_with_range( 0 , 99999 , 1 );
+  gtk_box_pack_start( GTK_BOX(hbox) , desc_label , FALSE , FALSE , 0 );
+  gtk_box_pack_start( GTK_BOX(hbox) , spinbt , FALSE , FALSE , 0 );
+  g_object_set_data( G_OBJECT(hbox) , "spinbt" , spinbt );
+  return hbox;
+}
+
+
+static void
+aosd_cb_configure_animation_timing_commit ( GtkWidget * timing_hbox , aosd_cfg_t * cfg )
+{
+  cfg->osd->animation.timing_display = gtk_spin_button_get_value_as_int(
+    GTK_SPIN_BUTTON(g_object_get_data(G_OBJECT(timing_hbox),"display")) );
+  cfg->osd->animation.timing_fadein = gtk_spin_button_get_value_as_int(
+    GTK_SPIN_BUTTON(g_object_get_data(G_OBJECT(timing_hbox),"fadein")) );
+  cfg->osd->animation.timing_fadeout = gtk_spin_button_get_value_as_int(
+    GTK_SPIN_BUTTON(g_object_get_data(G_OBJECT(timing_hbox),"fadeout")) );
+  return;
+}
+
+
+static GtkWidget *
+aosd_ui_configure_animation ( aosd_cfg_t * cfg , GList ** cb_list )
+{
+  GtkWidget *ani_vbox;
+  GtkWidget *ani_timing_frame, *ani_timing_hbox;
+  GtkWidget *ani_timing_fadein_widget, *ani_timing_fadeout_widget, *ani_timing_stay_widget;
+  GtkSizeGroup *sizegroup;
+
+  ani_vbox = gtk_vbox_new( FALSE , 0 );
+  gtk_container_set_border_width( GTK_CONTAINER(ani_vbox) , 6 );
+
+  ani_timing_hbox = gtk_hbox_new( FALSE , 0 );
+  ani_timing_frame = gtk_frame_new( _("Timing (ms)") );
+  gtk_container_set_border_width( GTK_CONTAINER(ani_timing_hbox) , 6 );
+  gtk_container_add( GTK_CONTAINER(ani_timing_frame) , ani_timing_hbox );
+  gtk_box_pack_start( GTK_BOX(ani_vbox) , ani_timing_frame , FALSE , FALSE , 0 );
+
+  ani_timing_stay_widget = aosd_ui_configure_animation_timing( _("Display:") );
+  gtk_spin_button_set_value( GTK_SPIN_BUTTON(g_object_get_data(
+    G_OBJECT(ani_timing_stay_widget),"spinbt")) , cfg->osd->animation.timing_display );
+  gtk_box_pack_start( GTK_BOX(ani_timing_hbox) , ani_timing_stay_widget , TRUE , TRUE , 0 );
+  gtk_box_pack_start( GTK_BOX(ani_timing_hbox) , gtk_vseparator_new() , FALSE , FALSE , 4 );
+  ani_timing_fadein_widget = aosd_ui_configure_animation_timing( _("Fade in:") );
+  gtk_spin_button_set_value( GTK_SPIN_BUTTON(g_object_get_data(
+    G_OBJECT(ani_timing_fadein_widget),"spinbt")) , cfg->osd->animation.timing_fadein );
+  gtk_box_pack_start( GTK_BOX(ani_timing_hbox) , ani_timing_fadein_widget , TRUE , TRUE , 0 );
+  gtk_box_pack_start( GTK_BOX(ani_timing_hbox) , gtk_vseparator_new() , FALSE , FALSE , 4 );
+  ani_timing_fadeout_widget = aosd_ui_configure_animation_timing( _("Fade out:") );
+  gtk_spin_button_set_value( GTK_SPIN_BUTTON(g_object_get_data(
+    G_OBJECT(ani_timing_fadeout_widget),"spinbt")) , cfg->osd->animation.timing_fadeout );
+  gtk_box_pack_start( GTK_BOX(ani_timing_hbox) , ani_timing_fadeout_widget , TRUE , TRUE , 0 );
+  g_object_set_data( G_OBJECT(ani_timing_hbox) , "display" ,
+    g_object_get_data(G_OBJECT(ani_timing_stay_widget),"spinbt") );
+  g_object_set_data( G_OBJECT(ani_timing_hbox) , "fadein" ,
+    g_object_get_data(G_OBJECT(ani_timing_fadein_widget),"spinbt") );
+  g_object_set_data( G_OBJECT(ani_timing_hbox) , "fadeout" ,
+    g_object_get_data(G_OBJECT(ani_timing_fadeout_widget),"spinbt") );
+  sizegroup = gtk_size_group_new( GTK_SIZE_GROUP_HORIZONTAL );
+  gtk_size_group_add_widget( sizegroup , ani_timing_stay_widget );
+  gtk_size_group_add_widget( sizegroup , ani_timing_fadein_widget );
+  gtk_size_group_add_widget( sizegroup , ani_timing_fadeout_widget );
+  aosd_callback_list_add( cb_list , ani_timing_hbox , aosd_cb_configure_animation_timing_commit );
+
+  return ani_vbox;
+}
+
+
+static void
+aosd_cb_configure_text_font_shadow_toggle ( GtkToggleButton * shadow_togglebt ,
+                                            gpointer shadow_colorbt )
+{
+  if ( gtk_toggle_button_get_active( shadow_togglebt ) == TRUE )
+    gtk_widget_set_sensitive( GTK_WIDGET(shadow_colorbt) , TRUE );
+  else
+    gtk_widget_set_sensitive( GTK_WIDGET(shadow_colorbt) , FALSE );
+  return;
+}
+
+
+static void
+aosd_cb_configure_text_font_commit ( GtkWidget * fontbt , aosd_cfg_t * cfg )
+{
+  gint fontnum = GPOINTER_TO_INT(g_object_get_data( G_OBJECT(fontbt) , "fontnum" ));
+  GdkColor color, shadow_color;
+  cfg->osd->text.fonts_name[fontnum] = g_strdup( gtk_font_button_get_font_name(GTK_FONT_BUTTON(fontbt)) );
+  gtk_color_button_get_color(
+    GTK_COLOR_BUTTON(g_object_get_data(G_OBJECT(fontbt),"color")) , &color );
+  cfg->osd->text.fonts_color[fontnum].red = color.red;
+  cfg->osd->text.fonts_color[fontnum].green = color.green;
+  cfg->osd->text.fonts_color[fontnum].blue = color.blue;
+  cfg->osd->text.fonts_color[fontnum].alpha = gtk_color_button_get_alpha(
+    GTK_COLOR_BUTTON(g_object_get_data(G_OBJECT(fontbt),"color")) );
+  cfg->osd->text.fonts_draw_shadow[fontnum] = gtk_toggle_button_get_active(
+    GTK_TOGGLE_BUTTON(g_object_get_data(G_OBJECT(fontbt),"use_shadow")) );
+  gtk_color_button_get_color(
+    GTK_COLOR_BUTTON(g_object_get_data(G_OBJECT(fontbt),"shadow_color")) , &shadow_color );
+  cfg->osd->text.fonts_shadow_color[fontnum].red = shadow_color.red;
+  cfg->osd->text.fonts_shadow_color[fontnum].green = shadow_color.green;
+  cfg->osd->text.fonts_shadow_color[fontnum].blue = shadow_color.blue;
+  cfg->osd->text.fonts_shadow_color[fontnum].alpha = gtk_color_button_get_alpha(
+    GTK_COLOR_BUTTON(g_object_get_data(G_OBJECT(fontbt),"shadow_color")) );
+  return;
+}
+
+
+static GtkWidget *
+aosd_ui_configure_text ( aosd_cfg_t * cfg , GList ** cb_list )
+{
+  GtkWidget *tex_vbox;
+  GtkWidget *tex_font_table, *tex_font_frame;
+  GtkWidget *tex_font_label[3], *tex_font_fontbt[3];
+  GtkWidget *tex_font_colorbt[3], *tex_font_shadow_togglebt[3];
+  GtkWidget *tex_font_shadow_colorbt[3];
+  GtkWidget *other_vbox;
+  gint i = 0;
+
+  tex_vbox = gtk_vbox_new( FALSE , 4 );
+  gtk_container_set_border_width( GTK_CONTAINER(tex_vbox) , 6 );
+
+  tex_font_frame = gtk_frame_new( _("Fonts") );
+  tex_font_table = gtk_table_new( 3 , 5 , FALSE );
+  gtk_container_set_border_width( GTK_CONTAINER(tex_font_table) , 6 );
+  gtk_table_set_row_spacings( GTK_TABLE(tex_font_table) , 4 );
+  gtk_table_set_col_spacings( GTK_TABLE(tex_font_table) , 4 );
+  for ( i = 0 ; i < AOSD_TEXT_FONTS_NUM ; i++ )
+  {
+    GdkColor gcolor = { 0 , 0 , 0 , 0 };
+    gchar *label_str = g_strdup_printf( "Font %i:" , i+1 );
+    tex_font_label[i] = gtk_label_new( label_str );
+    g_free( label_str );
+    tex_font_fontbt[i] = gtk_font_button_new();
+    gtk_font_button_set_show_style( GTK_FONT_BUTTON(tex_font_fontbt[i]) , TRUE );
+    gtk_font_button_set_show_size( GTK_FONT_BUTTON(tex_font_fontbt[i]) , TRUE );
+    gtk_font_button_set_use_font( GTK_FONT_BUTTON(tex_font_fontbt[i]) , FALSE );
+    gtk_font_button_set_use_size( GTK_FONT_BUTTON(tex_font_fontbt[i]) , FALSE );
+    gtk_font_button_set_font_name( GTK_FONT_BUTTON(tex_font_fontbt[i]) , cfg->osd->text.fonts_name[i] );
+    tex_font_colorbt[i] = gtk_color_button_new();
+    gcolor.red = cfg->osd->text.fonts_color[i].red;
+    gcolor.green = cfg->osd->text.fonts_color[i].green;
+    gcolor.blue = cfg->osd->text.fonts_color[i].blue;
+    gtk_color_button_set_use_alpha( GTK_COLOR_BUTTON(tex_font_colorbt[i]) , TRUE );
+    gtk_color_button_set_color( GTK_COLOR_BUTTON(tex_font_colorbt[i]) , &gcolor );
+    gtk_color_button_set_alpha( GTK_COLOR_BUTTON(tex_font_colorbt[i]) ,
+      cfg->osd->text.fonts_color[i].alpha );
+    tex_font_shadow_togglebt[i] = gtk_toggle_button_new_with_label( _("Shadow") );
+    gtk_toggle_button_set_mode( GTK_TOGGLE_BUTTON(tex_font_shadow_togglebt[i]) , FALSE );
+    tex_font_shadow_colorbt[i] = gtk_color_button_new();
+    gtk_color_button_set_use_alpha( GTK_COLOR_BUTTON(tex_font_shadow_colorbt[i]) , TRUE );
+    gcolor.red = cfg->osd->text.fonts_shadow_color[i].red;
+    gcolor.green = cfg->osd->text.fonts_shadow_color[i].green;
+    gcolor.blue = cfg->osd->text.fonts_shadow_color[i].blue;
+    gtk_color_button_set_color( GTK_COLOR_BUTTON(tex_font_shadow_colorbt[i]) , &gcolor );
+    gtk_color_button_set_alpha( GTK_COLOR_BUTTON(tex_font_shadow_colorbt[i]) ,
+      cfg->osd->text.fonts_shadow_color[i].alpha );
+    gtk_widget_set_sensitive( tex_font_shadow_colorbt[i] , FALSE );
+    g_signal_connect( G_OBJECT(tex_font_shadow_togglebt[i]) , "toggled" ,
+                      G_CALLBACK(aosd_cb_configure_text_font_shadow_toggle) ,
+                      tex_font_shadow_colorbt[i] );
+    gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON(tex_font_shadow_togglebt[i]) ,
+      cfg->osd->text.fonts_draw_shadow[i] );
+    gtk_table_attach( GTK_TABLE(tex_font_table) , tex_font_label[i] ,
+      0 , 1 , i , i + 1 , GTK_FILL , GTK_FILL , 0 , 0 );
+    gtk_table_attach( GTK_TABLE(tex_font_table) , tex_font_fontbt[i] ,
+      1 , 2 , i , i + 1 , GTK_FILL | GTK_EXPAND , GTK_FILL , 0 , 0 );
+    gtk_table_attach( GTK_TABLE(tex_font_table) , tex_font_colorbt[i] ,
+      2 , 3 , i , i + 1 , GTK_FILL , GTK_FILL , 0 , 0 );
+    gtk_table_attach( GTK_TABLE(tex_font_table) , tex_font_shadow_togglebt[i] ,
+      3 , 4 , i , i + 1 , GTK_FILL , GTK_FILL , 0 , 0 );
+    gtk_table_attach( GTK_TABLE(tex_font_table) , tex_font_shadow_colorbt[i] ,
+      4 , 5 , i , i + 1 , GTK_FILL , GTK_FILL , 0 , 0 );
+    g_object_set_data( G_OBJECT(tex_font_fontbt[i]) , "fontnum" , GINT_TO_POINTER(i) );
+    g_object_set_data( G_OBJECT(tex_font_fontbt[i]) , "color" , tex_font_colorbt[i] );
+    g_object_set_data( G_OBJECT(tex_font_fontbt[i]) , "use_shadow" , tex_font_shadow_togglebt[i] );
+    g_object_set_data( G_OBJECT(tex_font_fontbt[i]) , "shadow_color" , tex_font_shadow_colorbt[i] );
+    aosd_callback_list_add( cb_list , tex_font_fontbt[i] , aosd_cb_configure_text_font_commit );
+  }
+  gtk_container_add( GTK_CONTAINER(tex_font_frame) , tex_font_table );
+  gtk_box_pack_start( GTK_BOX(tex_vbox) , tex_font_frame , FALSE , FALSE , 0 );
+
+  return tex_vbox;
+}
+
+
+static void
+aosd_ui_configure_decoration_browse ( GtkButton * button , gpointer entry )
+{
+  GtkWidget *dialog;
+  GtkWidget *parent_win = gtk_widget_get_toplevel( GTK_WIDGET(button) );
+  dialog = gtk_file_chooser_dialog_new ( _("Select Skin File") ,
+                                         ( GTK_WIDGET_TOPLEVEL(parent_win) ? GTK_WINDOW(parent_win) : NULL ) ,
+                                         GTK_FILE_CHOOSER_ACTION_OPEN ,
+                                         GTK_STOCK_CANCEL , GTK_RESPONSE_CANCEL ,
+                                         GTK_STOCK_OPEN , GTK_RESPONSE_ACCEPT , NULL );
+  if ( gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT )
+  {
+    gchar *filename;
+    filename = gtk_file_chooser_get_filename( GTK_FILE_CHOOSER(dialog) );
+    gtk_entry_set_text( GTK_ENTRY(entry) , filename );
+    g_free( filename );
+  }
+  gtk_widget_destroy( dialog );
+  return;
+}
+
+
+static void
+aosd_cb_configure_decoration_style_commit ( GtkWidget * lv , aosd_cfg_t * cfg )
+{
+  GtkTreeSelection *sel = gtk_tree_view_get_selection( GTK_TREE_VIEW(lv) );
+  GtkTreeModel *model;
+  GtkTreeIter iter;
+
+  if ( gtk_tree_selection_get_selected( sel , &model , &iter ) == TRUE )
+  {
+    gint deco_code = 0;
+    gtk_tree_model_get( model , &iter , 1 , &deco_code , -1 );
+    cfg->osd->decoration.code = deco_code;
+  }
+  return;
+}
+
+
+static void
+aosd_cb_configure_decoration_color_commit ( GtkWidget * colorbt , aosd_cfg_t * cfg )
+{
+  GdkColor gcolor;
+  aosd_color_t color;
+  gint colnum = GPOINTER_TO_INT( g_object_get_data( G_OBJECT(colorbt) , "colnum" ) );
+  gtk_color_button_get_color( GTK_COLOR_BUTTON(colorbt) , &gcolor );
+  color.red = gcolor.red;
+  color.green = gcolor.green;
+  color.blue = gcolor.blue;
+  color.alpha = gtk_color_button_get_alpha( GTK_COLOR_BUTTON(colorbt) );
+  g_array_insert_val( cfg->osd->decoration.colors , colnum , color );
+  return;
+}
+
+
+static void
+aosd_cb_configure_decoration_skinfile_commit ( GtkWidget * entry , aosd_cfg_t * cfg )
+{
+  cfg->osd->decoration.skin_file = g_strdup( gtk_entry_get_text(GTK_ENTRY(entry)) );
+  return;
+}
+
+
+static GtkWidget *
+aosd_ui_configure_decoration ( aosd_cfg_t * cfg , GList ** cb_list )
+{
+  GtkWidget *dec_hbox;
+  GtkWidget *dec_rstyle_lv, *dec_rstyle_lv_frame, *dec_rstyle_lv_sw;
+  GtkListStore *dec_rstyle_store;
+  GtkCellRenderer *dec_rstyle_lv_rndr_text;
+  GtkTreeViewColumn *dec_rstyle_lv_col_desc;
+  GtkTreeSelection *dec_rstyle_lv_sel;
+  GtkTreeIter iter, iter_sel;
+  GtkWidget *dec_rstyle_hbox;
+  GtkWidget *dec_rstyleopts_frame, *dec_rstyleopts_table;
+  GtkWidget *dec_rstylecustom_frame, *dec_rstylecustom_table;
+  GtkWidget *dec_rstylecustom_label, *dec_rstylecustom_entry, *dec_rstylecustom_browse_bt;
+  gint *deco_code_array, deco_code_array_size;
+  gint colors_max_num = 0, i = 0;
+
+  dec_hbox = gtk_hbox_new( FALSE , 4 );
+  gtk_container_set_border_width( GTK_CONTAINER(dec_hbox) , 6 );
+
+  /* decoration style model
+     ---------------------------------------------
+     G_TYPE_STRING -> decoration description
+     G_TYPE_INT -> decoration code
+     G_TYPE_INT -> number of user-definable colors
+     ---------------------------------------------
+  */
+  dec_rstyle_store = gtk_list_store_new( 3 , G_TYPE_STRING , G_TYPE_INT , G_TYPE_INT );
+  aosd_deco_style_get_codes_array ( &deco_code_array , &deco_code_array_size );
+  for ( i = 0 ; i < deco_code_array_size ; i++ )
+  {
+    gint colors_num = aosd_deco_style_get_numcol( deco_code_array[i] );
+    if ( colors_num > colors_max_num )
+      colors_max_num = colors_num;
+    gtk_list_store_append( dec_rstyle_store , &iter );
+    gtk_list_store_set( dec_rstyle_store , &iter ,
+      0 , aosd_deco_style_get_desc( deco_code_array[i] ) ,
+      1 , deco_code_array[i] , 2 , colors_num , -1 );
+    if ( deco_code_array[i] == cfg->osd->decoration.code )
+      iter_sel = iter;
+  }
+
+  dec_rstyle_lv_frame = gtk_frame_new( NULL );
+  dec_rstyle_lv = gtk_tree_view_new_with_model( GTK_TREE_MODEL(dec_rstyle_store) );
+  g_object_unref( dec_rstyle_store );
+  dec_rstyle_lv_sel = gtk_tree_view_get_selection( GTK_TREE_VIEW(dec_rstyle_lv) );
+  gtk_tree_selection_set_mode( dec_rstyle_lv_sel , GTK_SELECTION_BROWSE );
+
+  dec_rstyle_lv_rndr_text = gtk_cell_renderer_text_new();
+  dec_rstyle_lv_col_desc = gtk_tree_view_column_new_with_attributes(
+    _("Render Style") , dec_rstyle_lv_rndr_text , "text" , 0 , NULL );
+  gtk_tree_view_append_column( GTK_TREE_VIEW(dec_rstyle_lv), dec_rstyle_lv_col_desc );
+  dec_rstyle_lv_sw = gtk_scrolled_window_new( NULL , NULL );
+  gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW(dec_rstyle_lv_sw) ,
+                                  GTK_POLICY_NEVER , GTK_POLICY_ALWAYS );
+  gtk_container_add( GTK_CONTAINER(dec_rstyle_lv_sw) , dec_rstyle_lv );
+  gtk_container_add( GTK_CONTAINER(dec_rstyle_lv_frame) , dec_rstyle_lv_sw );
+
+  gtk_tree_selection_select_iter( dec_rstyle_lv_sel , &iter_sel );
+  gtk_box_pack_start( GTK_BOX(dec_hbox) , dec_rstyle_lv_frame , FALSE , FALSE , 0 );
+  aosd_callback_list_add( cb_list , dec_rstyle_lv , aosd_cb_configure_decoration_style_commit );
+
+  dec_rstyle_hbox = gtk_vbox_new( FALSE , 4 );
+  gtk_box_pack_start( GTK_BOX(dec_hbox) , dec_rstyle_hbox , TRUE , TRUE , 0 );
+
+  /* in colors_max_num now there's the maximum number of colors used by decoration styles */
+  dec_rstyleopts_frame = gtk_frame_new( _("Colors") );
+  dec_rstyleopts_table = gtk_table_new( (colors_max_num / 3) + 1 , 3 , TRUE );
+  gtk_container_set_border_width( GTK_CONTAINER(dec_rstyleopts_table) , 6 );
+  gtk_table_set_row_spacings( GTK_TABLE(dec_rstyleopts_table) , 4 );
+  gtk_table_set_col_spacings( GTK_TABLE(dec_rstyleopts_table) , 8 );
+  gtk_container_add( GTK_CONTAINER(dec_rstyleopts_frame) , dec_rstyleopts_table );
+  for ( i = 0 ; i < colors_max_num ; i++ )
+  {
+    GtkWidget *colorbt, *hbox, *label;
+    aosd_color_t color = g_array_index( cfg->osd->decoration.colors , aosd_color_t , i );
+    GdkColor gcolor = { 0 , 0 , 0 , 0 };
+    gchar *label_str = NULL;
+    hbox = gtk_hbox_new( FALSE , 4 );
+    label_str = g_strdup_printf( "Color %i:" , i+1 );
+    label = gtk_label_new( label_str );
+    g_free( label_str );
+    colorbt = gtk_color_button_new();
+    gtk_color_button_set_use_alpha( GTK_COLOR_BUTTON(colorbt) , TRUE );
+    gcolor.red = color.red; gcolor.green = color.green; gcolor.blue = color.blue;
+    gtk_color_button_set_color( GTK_COLOR_BUTTON(colorbt) , &gcolor );
+    gtk_color_button_set_alpha( GTK_COLOR_BUTTON(colorbt) , color.alpha );
+    gtk_box_pack_start( GTK_BOX(hbox) , label , FALSE , FALSE , 0 );
+    gtk_box_pack_start( GTK_BOX(hbox) , colorbt , FALSE , FALSE , 0 );
+    gtk_table_attach( GTK_TABLE(dec_rstyleopts_table) , hbox ,
+      (i % 3) , (i % 3) + 1 , (i / 3) , (i / 3) + 1 ,
+      GTK_FILL , GTK_FILL , 0 , 0 );
+    g_object_set_data( G_OBJECT(colorbt) , "colnum" , GINT_TO_POINTER(i) );
+    aosd_callback_list_add( cb_list , colorbt , aosd_cb_configure_decoration_color_commit );
+  }
+  gtk_box_pack_start( GTK_BOX(dec_rstyle_hbox) , dec_rstyleopts_frame , FALSE , FALSE , 0 );
+
+#if 0
+  /* custom skin entry TODO still working on this */
+  dec_rstylecustom_frame = gtk_frame_new( _("Custom Skin") );
+  dec_rstylecustom_table = gtk_table_new( 1 , 3 , FALSE );
+  gtk_container_set_border_width( GTK_CONTAINER(dec_rstylecustom_table) , 6 );
+  gtk_table_set_row_spacings( GTK_TABLE(dec_rstylecustom_table) , 4 );
+  gtk_table_set_col_spacings( GTK_TABLE(dec_rstylecustom_table) , 4 );
+  gtk_container_add( GTK_CONTAINER(dec_rstylecustom_frame) , dec_rstylecustom_table );
+  dec_rstylecustom_label = gtk_label_new( _("Skin file:") );
+  dec_rstylecustom_entry = gtk_entry_new();
+  gtk_entry_set_text( GTK_ENTRY(dec_rstylecustom_entry) , cfg->osd->decoration.skin_file );
+  dec_rstylecustom_browse_bt = gtk_button_new_with_label( _("Browse") );
+  g_signal_connect( G_OBJECT(dec_rstylecustom_browse_bt) , "clicked" ,
+                    G_CALLBACK(aosd_ui_configure_decoration_browse) , dec_rstylecustom_entry );
+  gtk_table_attach( GTK_TABLE(dec_rstylecustom_table) , dec_rstylecustom_label ,
+    0 , 1 , 0 , 1 , GTK_FILL , GTK_FILL , 0 , 0 );
+  gtk_table_attach( GTK_TABLE(dec_rstylecustom_table) , dec_rstylecustom_entry ,
+    1 , 2 , 0 , 1 , GTK_FILL | GTK_EXPAND , GTK_FILL , 0 , 0 );
+  gtk_table_attach( GTK_TABLE(dec_rstylecustom_table) , dec_rstylecustom_browse_bt ,
+    2 , 3 , 0 , 1 , GTK_FILL , GTK_FILL , 0 , 0 );
+  aosd_callback_list_add( cb_list , dec_rstylecustom_entry , aosd_cb_configure_decoration_skinfile_commit );
+
+  gtk_box_pack_start( GTK_BOX(dec_rstyle_hbox) , dec_rstylecustom_frame , FALSE , FALSE , 0 );
+#endif
+
+  return dec_hbox;
+}
+
+
+static GtkWidget *
+aosd_ui_configure_trigger ( aosd_cfg_t * cfg , GList ** cb_list )
+{
+  GtkWidget *tri_hbox;
+  GtkWidget *tri_event_lv, *tri_event_lv_frame, *tri_event_lv_sw;
+  GtkListStore *tri_event_store;
+  GtkCellRenderer *tri_event_lv_rndr_text;
+  GtkTreeViewColumn *tri_event_lv_col_desc;
+  GtkTreeSelection *tri_event_lv_sel;
+  GtkTreeIter iter;
+  GtkWidget *tri_event_label;
+
+  tri_hbox = gtk_hbox_new( FALSE , 4 );
+  gtk_container_set_border_width( GTK_CONTAINER(tri_hbox) , 6 );
+
+  /* TODO this part will probably be changed in future! */
+
+  /* event model
+     ---------------------------------------------
+     G_TYPE_STRING -> decoration description
+     ---------------------------------------------
+  */
+  tri_event_store = gtk_list_store_new( 1 , G_TYPE_STRING );
+  gtk_list_store_append( tri_event_store , &iter );
+  gtk_list_store_set( tri_event_store , &iter , 0 , _("Song Change") , -1 );
+
+  tri_event_lv_frame = gtk_frame_new( NULL );
+  tri_event_lv = gtk_tree_view_new_with_model( GTK_TREE_MODEL(tri_event_store) );
+  g_object_unref( tri_event_store );
+  tri_event_lv_sel = gtk_tree_view_get_selection( GTK_TREE_VIEW(tri_event_lv) );
+  gtk_tree_selection_set_mode( tri_event_lv_sel , GTK_SELECTION_BROWSE );
+
+  tri_event_lv_rndr_text = gtk_cell_renderer_text_new();
+  tri_event_lv_col_desc = gtk_tree_view_column_new_with_attributes(
+    _("Event") , tri_event_lv_rndr_text , "text" , 0 , NULL );
+  gtk_tree_view_append_column( GTK_TREE_VIEW(tri_event_lv), tri_event_lv_col_desc );
+  tri_event_lv_sw = gtk_scrolled_window_new( NULL , NULL );
+  gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW(tri_event_lv_sw) ,
+                                  GTK_POLICY_NEVER , GTK_POLICY_ALWAYS );
+  gtk_container_add( GTK_CONTAINER(tri_event_lv_sw) , tri_event_lv );
+  gtk_container_add( GTK_CONTAINER(tri_event_lv_frame) , tri_event_lv_sw );
+  gtk_tree_selection_select_iter( tri_event_lv_sel , &iter );
+
+  gtk_box_pack_start( GTK_BOX(tri_hbox) , tri_event_lv_frame , FALSE , FALSE , 0 );
+
+  tri_event_label = gtk_label_new( _("Song Change is currently the only event that triggers "
+    "the OSD. Other events will be added in next Audacious OSD versions.") );
+  gtk_label_set_line_wrap( GTK_LABEL(tri_event_label) , TRUE );
+  gtk_misc_set_alignment( GTK_MISC(tri_event_label) , 0.5 , 0.0 );
+  gtk_box_pack_start( GTK_BOX(tri_hbox) , tri_event_label , FALSE , FALSE , 0 );
+
+  return tri_hbox;
+}
+
+
+static void
+aosd_cb_configure_test ( gpointer cfg_win )
+{
+  gchar *markup_message = NULL;
+  aosd_cfg_t *cfg = aosd_cfg_new();
+  GList *cb_list = g_object_get_data( G_OBJECT(cfg_win) , "cblist" );
+  aosd_callback_list_run( cb_list , cfg );
+  cfg->set = TRUE;
+#ifdef DEBUG
+  aosd_cfg_debug( cfg );
+#endif
+  markup_message = g_markup_printf_escaped(
+    "<span font_desc='%s'>Audacious OSD</span>" , cfg->osd->text.fonts_name[0] );
+  aosd_display( markup_message , cfg->osd , TRUE );
+  g_free( markup_message );
+  aosd_cfg_delete( cfg );
+  return;
+}
+
+
+static void
+aosd_cb_configure_cancel ( gpointer cfg_win )
+{
+  GList *cb_list = g_object_get_data( G_OBJECT(cfg_win) , "cblist" );
+  aosd_callback_list_free( cb_list );
+  aosd_shutdown(); /* stop any displayed osd */
+  gtk_widget_destroy( GTK_WIDGET(cfg_win) );
+  return;
+}
+
+
+static void
+aosd_cb_configure_ok ( gpointer cfg_win )
+{
+  gchar *markup_message = NULL;
+  aosd_cfg_t *cfg = aosd_cfg_new();
+  GList *cb_list = g_object_get_data( G_OBJECT(cfg_win) , "cblist" );
+  aosd_callback_list_run( cb_list , cfg );
+  cfg->set = TRUE;
+  aosd_shutdown(); /* stop any displayed osd */
+  if ( global_config != NULL )
+  {
+    /* plugin is active */
+    aosd_cfg_delete( global_config ); /* delete old global_config */
+    global_config = cfg; /* put the new one */
+    aosd_cfg_save( cfg ); /* save the new configuration on config file */
+  }
+  else
+  {
+    /* plugin is not active */
+    aosd_cfg_save( cfg ); /* save the new configuration on config file */
+  }
+  aosd_callback_list_free( cb_list );
+  gtk_widget_destroy( GTK_WIDGET(cfg_win) );
+  return;
+}
+
+
+void
+aosd_ui_configure ( aosd_cfg_t * cfg )
+{
+  static GtkWidget *cfg_win = NULL;
+  GtkWidget *cfg_vbox;
+  GtkWidget *cfg_nb;
+  GtkWidget *cfg_bbar_hbbox;
+  GtkWidget *cfg_bbar_bt_ok, *cfg_bbar_bt_test, *cfg_bbar_bt_cancel;
+  GtkWidget *cfg_position_widget;
+  GtkWidget *cfg_animation_widget;
+  GtkWidget *cfg_text_widget;
+  GtkWidget *cfg_decoration_widget;
+  GtkWidget *cfg_trigger_widget;
+  GdkGeometry cfg_win_hints;
+  GList *cb_list = NULL; /* list of custom callbacks */
+
+  if ( cfg_win != NULL )
+  {
+    gtk_window_present( GTK_WINDOW(cfg_win) );
+    return;
+  }
+
+  cfg_win = gtk_window_new( GTK_WINDOW_TOPLEVEL );
+  gtk_window_set_type_hint( GTK_WINDOW(cfg_win), GDK_WINDOW_TYPE_HINT_DIALOG );
+  gtk_window_set_title( GTK_WINDOW(cfg_win) , _("Audacious OSD - configuration") );
+  gtk_container_set_border_width( GTK_CONTAINER(cfg_win), 10 );
+  g_signal_connect( G_OBJECT(cfg_win) , "destroy" ,
+                    G_CALLBACK(gtk_widget_destroyed) , &cfg_win );
+  cfg_win_hints.min_width = -1;
+  cfg_win_hints.min_height = 350;
+  gtk_window_set_geometry_hints( GTK_WINDOW(cfg_win) , GTK_WIDGET(cfg_win) ,
+                                 &cfg_win_hints , GDK_HINT_MIN_SIZE );
+
+  cfg_vbox = gtk_vbox_new( 0 , FALSE );
+  gtk_container_add( GTK_CONTAINER(cfg_win) , cfg_vbox );
+
+  cfg_nb = gtk_notebook_new();
+  gtk_notebook_set_tab_pos( GTK_NOTEBOOK(cfg_nb) , GTK_POS_TOP );
+  gtk_box_pack_start( GTK_BOX(cfg_vbox) , cfg_nb , TRUE , TRUE , 0 );
+
+  gtk_box_pack_start( GTK_BOX(cfg_vbox) , gtk_hseparator_new() , FALSE , FALSE , 4 );
+
+  cfg_bbar_hbbox = gtk_hbutton_box_new();
+  gtk_button_box_set_layout( GTK_BUTTON_BOX(cfg_bbar_hbbox) , GTK_BUTTONBOX_START );
+  gtk_box_pack_start( GTK_BOX(cfg_vbox) , cfg_bbar_hbbox , FALSE , FALSE , 0 );
+  cfg_bbar_bt_test = gtk_button_new_with_label( _("Test") );
+  gtk_button_set_image( GTK_BUTTON(cfg_bbar_bt_test) ,
+    gtk_image_new_from_stock( GTK_STOCK_MEDIA_PLAY , GTK_ICON_SIZE_BUTTON ) );
+  gtk_container_add( GTK_CONTAINER(cfg_bbar_hbbox) , cfg_bbar_bt_test );
+  gtk_button_box_set_child_secondary( GTK_BUTTON_BOX(cfg_bbar_hbbox) , cfg_bbar_bt_test , FALSE );
+  cfg_bbar_bt_cancel = gtk_button_new_from_stock( GTK_STOCK_CANCEL );
+  gtk_container_add( GTK_CONTAINER(cfg_bbar_hbbox) , cfg_bbar_bt_cancel );
+  gtk_button_box_set_child_secondary( GTK_BUTTON_BOX(cfg_bbar_hbbox) , cfg_bbar_bt_cancel , TRUE );
+  cfg_bbar_bt_ok = gtk_button_new_from_stock( GTK_STOCK_OK );
+  gtk_container_add( GTK_CONTAINER(cfg_bbar_hbbox) , cfg_bbar_bt_ok );
+  gtk_button_box_set_child_secondary( GTK_BUTTON_BOX(cfg_bbar_hbbox) , cfg_bbar_bt_ok , TRUE );
+
+  /* add POSITION page */
+  cfg_position_widget = aosd_ui_configure_position( cfg , &cb_list );
+  gtk_notebook_append_page( GTK_NOTEBOOK(cfg_nb) ,
+    cfg_position_widget , gtk_label_new( _("Position") ) );
+
+  /* add ANIMATION page */
+  cfg_animation_widget = aosd_ui_configure_animation( cfg , &cb_list );
+  gtk_notebook_append_page( GTK_NOTEBOOK(cfg_nb) ,
+    cfg_animation_widget , gtk_label_new( _("Animation") ) );
+
+  /* add TEXT page */
+  cfg_text_widget = aosd_ui_configure_text( cfg , &cb_list );
+  gtk_notebook_append_page( GTK_NOTEBOOK(cfg_nb) ,
+    cfg_text_widget , gtk_label_new( _("Text") ) );
+
+  /* add DECORATION page */
+  cfg_decoration_widget = aosd_ui_configure_decoration( cfg , &cb_list );
+  gtk_notebook_append_page( GTK_NOTEBOOK(cfg_nb) ,
+    cfg_decoration_widget , gtk_label_new( _("Decoration") ) );
+
+  /* add TRIGGER page */
+  cfg_trigger_widget = aosd_ui_configure_trigger( cfg , &cb_list );
+  gtk_notebook_append_page( GTK_NOTEBOOK(cfg_nb) ,
+    cfg_trigger_widget , gtk_label_new( _("Trigger") ) );
+
+  g_object_set_data( G_OBJECT(cfg_win) , "cblist" , cb_list );
+
+  g_signal_connect_swapped( G_OBJECT(cfg_bbar_bt_test) , "clicked" ,
+                            G_CALLBACK(aosd_cb_configure_test) , cfg_win );
+  g_signal_connect_swapped( G_OBJECT(cfg_bbar_bt_cancel) , "clicked" ,
+                            G_CALLBACK(aosd_cb_configure_cancel) , cfg_win );
+  g_signal_connect_swapped( G_OBJECT(cfg_bbar_bt_ok) , "clicked" ,
+                            G_CALLBACK(aosd_cb_configure_ok) , cfg_win );
+
+  gtk_widget_show_all( cfg_win );
+}
+
+
+/* about box */
+void
+aosd_ui_about ( void )
+{
+  static GtkWidget *about_win = NULL;
+  GtkWidget *about_vbox;
+  GtkWidget *logoandinfo_vbox;
+  GtkWidget *info_tv, *info_tv_sw, *info_tv_frame;
+  GtkWidget *bbar_bbox, *bbar_bt_ok;
+  GtkTextBuffer *info_tb;
+  GdkGeometry abount_win_hints;
+
+  if ( about_win != NULL )
+  {
+    gtk_window_present( GTK_WINDOW(about_win) );
+    return;
+  }
+
+  about_win = gtk_window_new( GTK_WINDOW_TOPLEVEL );
+  gtk_window_set_type_hint( GTK_WINDOW(about_win), GDK_WINDOW_TYPE_HINT_DIALOG );
+  gtk_window_set_position( GTK_WINDOW(about_win), GTK_WIN_POS_CENTER );
+  gtk_window_set_title( GTK_WINDOW(about_win), _("Audacious OSD - about") );
+  abount_win_hints.min_width = 420;
+  abount_win_hints.min_height = 240;
+  gtk_window_set_geometry_hints( GTK_WINDOW(about_win) , GTK_WIDGET(about_win) ,
+                                 &abount_win_hints , GDK_HINT_MIN_SIZE );
+  /* gtk_window_set_resizable( GTK_WINDOW(about_win) , FALSE ); */
+  gtk_container_set_border_width( GTK_CONTAINER(about_win) , 10 );
+  g_signal_connect( G_OBJECT(about_win) , "destroy" , G_CALLBACK(gtk_widget_destroyed) , &about_win );
+
+  about_vbox = gtk_vbox_new( FALSE , 0 );
+  gtk_container_add( GTK_CONTAINER(about_win) , about_vbox );
+
+  logoandinfo_vbox = gtk_vbox_new( TRUE , 2 );
+
+  /* TODO make a logo or make someone do it! :)
+  logo_pixbuf = gdk_pixbuf_new_from_xpm_data( (const gchar **)evdev_plug_logo_xpm );
+  logo_image = gtk_image_new_from_pixbuf( logo_pixbuf );
+  g_object_unref( logo_pixbuf );
+
+  logo_frame = gtk_frame_new( NULL );
+  gtk_container_add( GTK_CONTAINER(logo_frame) , logo_image );
+  gtk_box_pack_start( GTK_BOX(logoandinfo_vbox) , logo_frame , TRUE , TRUE , 0 );  */
+
+  info_tv = gtk_text_view_new();
+  info_tb = gtk_text_view_get_buffer( GTK_TEXT_VIEW(info_tv) );
+  gtk_text_view_set_editable( GTK_TEXT_VIEW(info_tv) , FALSE );
+  gtk_text_view_set_cursor_visible( GTK_TEXT_VIEW(info_tv) , FALSE );
+  gtk_text_view_set_justification( GTK_TEXT_VIEW(info_tv) , GTK_JUSTIFY_LEFT );
+  gtk_text_view_set_left_margin( GTK_TEXT_VIEW(info_tv) , 10 );
+
+  gtk_text_buffer_set_text( info_tb ,
+                            _("\nAudacious OSD " AOSD_VERSION_PLUGIN
+                              "\nhttp://www.develia.org/projects.php?p=aosd\n"
+                              "written by Giacomo Lozito\n"
+                              "< james@develia.org >\n\n"
+                              "On-Screen-Display is based on Ghosd library\n"
+                              "written by Evan Martin\n"
+                              "http://neugierig.org/software/ghosd/\n\n") , -1 );
+
+  info_tv_sw = gtk_scrolled_window_new( NULL , NULL );
+  gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW(info_tv_sw) ,
+                                  GTK_POLICY_NEVER , GTK_POLICY_ALWAYS );
+  gtk_container_add( GTK_CONTAINER(info_tv_sw) , info_tv );
+  info_tv_frame = gtk_frame_new( NULL );
+  gtk_container_add( GTK_CONTAINER(info_tv_frame) , info_tv_sw );
+  gtk_box_pack_start( GTK_BOX(logoandinfo_vbox) , info_tv_frame , TRUE , TRUE , 0 );
+
+  gtk_box_pack_start( GTK_BOX(about_vbox) , logoandinfo_vbox , TRUE , TRUE , 0 );
+
+  /* horizontal separator and buttons */
+  gtk_box_pack_start( GTK_BOX(about_vbox) , gtk_hseparator_new() , FALSE , FALSE , 4 );
+  bbar_bbox = gtk_hbutton_box_new();
+  gtk_button_box_set_layout( GTK_BUTTON_BOX(bbar_bbox) , GTK_BUTTONBOX_END );
+  bbar_bt_ok = gtk_button_new_from_stock( GTK_STOCK_OK );
+  g_signal_connect_swapped( G_OBJECT(bbar_bt_ok) , "clicked" ,
+                            G_CALLBACK(gtk_widget_destroy) , about_win );
+  gtk_container_add( GTK_CONTAINER(bbar_bbox) , bbar_bt_ok );
+  gtk_box_pack_start( GTK_BOX(about_vbox) , bbar_bbox , FALSE , FALSE , 0 );
+
+  gtk_widget_show_all( about_win );
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/aosd/aosd_ui.c.old	Mon Jan 29 06:40:04 2007 -0800
@@ -0,0 +1,159 @@
+/*
+*
+* Author: Giacomo Lozito <james@develia.org>, (C) 2005-2007
+*
+* This program is free software; you can redistribute it and/or modify it
+* under the terms of the GNU General Public License as published by the
+* Free Software Foundation; either version 2 of the License, or (at your
+* option) any later version.
+*
+* This program 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 this program; if not, write to the Free Software Foundation, Inc.,
+* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
+*
+*/
+
+#include "aosd_ui.h"
+#include <glib.h>
+#include <gdk/gdk.h>
+#include <gtk/gtk.h>
+
+
+gint
+aosd_display_osd ( void )
+{
+  GtkWidget *osd_win;
+  GtkWidget *osd_darea;
+  PangoLayout *osd_layout;
+  PangoFontDescription *osd_fontdesc;
+  GdkPixmap *osd_darea_pixmap, *osd_darea_bitmap;
+  GdkGC *osd_darea_gc;
+  GdkColor fg_color, bg_color, bitmap_color;
+  GdkColormap *bitmap_colormap;
+  gint max_width = 0, border_width = 1;
+  gint width = 0, height = 0;
+  gint off_x = 0, off_y = 0;
+  PangoAlignment osd_alignment = PANGO_ALIGN_CENTER;
+  gboolean translucent_bg = TRUE;
+
+  osd_win = gtk_window_new( GTK_WINDOW_POPUP );
+  gtk_widget_add_events( osd_win , GDK_ENTER_NOTIFY_MASK );
+  gtk_widget_realize( osd_win );
+
+  osd_darea = gtk_drawing_area_new();
+  gtk_container_add( GTK_CONTAINER(osd_win) , osd_darea );
+  gtk_widget_show( osd_darea );
+
+  osd_fontdesc = pango_font_description_from_string("Courier,Mono 28");
+
+  osd_layout = gtk_widget_create_pango_layout( osd_darea , "AUDACIOUS OSD 0.1 by Giacomo" );
+  pango_layout_set_ellipsize( osd_layout , PANGO_ELLIPSIZE_NONE );
+  pango_layout_set_justify( osd_layout , FALSE );
+  pango_layout_set_alignment( osd_layout , osd_alignment );
+  pango_layout_set_font_description( osd_layout , osd_fontdesc );
+
+  max_width = gdk_screen_get_width( gdk_screen_get_default() ) - 8;
+  pango_layout_set_width( osd_layout , PANGO_SCALE * max_width );
+  pango_layout_get_pixel_size( osd_layout , &width , &height );
+
+  off_x = border_width * 2;
+  off_y = border_width * 2;
+
+  if ( osd_alignment == PANGO_ALIGN_CENTER )
+    off_x -= max_width/2 - width/2;
+  else if ( osd_alignment == PANGO_ALIGN_RIGHT )
+    off_x -= max_width - width;
+
+  width += border_width * 4;
+  height += border_width * 4;
+
+  gtk_widget_set_size_request( osd_darea , width , height );
+  gtk_widget_realize( osd_darea );
+
+  osd_darea_pixmap = gdk_pixmap_new( GDK_DRAWABLE(osd_darea->window) , width , height , -1 );
+
+  osd_darea_gc = gdk_gc_new( GDK_DRAWABLE(osd_darea_pixmap) );
+  gdk_gc_copy( osd_darea_gc , osd_darea->style->fg_gc[GTK_STATE_NORMAL] );
+
+  gdk_color_parse( "Blue" , &fg_color ); /* TODO pass color as function param */
+  gdk_color_parse( "White" , &bg_color ); /* TODO pass color as function param */
+
+  gdk_gc_set_rgb_fg_color( osd_darea_gc , &bg_color );
+  gdk_draw_rectangle( GDK_DRAWABLE(osd_darea_pixmap) , osd_darea_gc , TRUE ,
+                      0 , 0 , width , height );
+  gdk_gc_set_rgb_fg_color( osd_darea_gc , &fg_color );
+  gdk_draw_layout( GDK_DRAWABLE(osd_darea_pixmap) , osd_darea_gc , off_x , off_y , osd_layout );
+  g_object_unref( osd_darea_gc );
+
+  osd_darea_bitmap = gdk_pixmap_new( GDK_DRAWABLE(osd_darea->window) , width , height , 1 );
+  osd_darea_gc = gdk_gc_new( GDK_DRAWABLE(osd_darea_bitmap) );
+
+  /* gdk will complain if we don't pass a colormap to osd_darea_gc */
+  gdk_gc_set_colormap( osd_darea_gc , gdk_colormap_get_system() );
+
+  bitmap_color.pixel = 0;
+  gdk_gc_set_foreground( osd_darea_gc , &bitmap_color );
+
+  if ( translucent_bg == TRUE )
+  {
+    gint w = 2, h = 2;
+    GdkPixmap *stipple_bitmap;
+
+    stipple_bitmap = gdk_pixmap_new( NULL , w , h , 1 );
+    bitmap_color.pixel = 0; gdk_gc_set_foreground( osd_darea_gc , &bitmap_color );
+    gdk_draw_rectangle( GDK_DRAWABLE(stipple_bitmap) , osd_darea_gc , TRUE , 0 , 0 , w , h );
+    bitmap_color.pixel = 1; gdk_gc_set_foreground( osd_darea_gc , &bitmap_color );
+    gdk_draw_point( GDK_DRAWABLE(stipple_bitmap) , osd_darea_gc , 0 , 0 );
+    gdk_draw_point( GDK_DRAWABLE(stipple_bitmap) , osd_darea_gc , 1 , 1 );
+    bitmap_color.pixel = 0; gdk_gc_set_foreground( osd_darea_gc , &bitmap_color );
+    gdk_draw_rectangle( GDK_DRAWABLE(osd_darea_bitmap) , osd_darea_gc , TRUE ,
+                        0 , 0 , width , height );
+    gdk_gc_set_stipple( osd_darea_gc , stipple_bitmap );
+    gdk_gc_set_fill( osd_darea_gc , GDK_STIPPLED );
+    bitmap_color.pixel = 1; gdk_gc_set_foreground( osd_darea_gc , &bitmap_color );
+    gdk_draw_rectangle( GDK_DRAWABLE(osd_darea_bitmap) , osd_darea_gc , TRUE ,
+                        0 , 0 , width , height );
+    gdk_gc_set_fill( osd_darea_gc , GDK_SOLID );
+  }
+  else
+    gdk_draw_rectangle( GDK_DRAWABLE(osd_darea_bitmap) , osd_darea_gc , TRUE ,
+                        0 , 0 , width , height );
+
+  bitmap_color.pixel = 1;
+  gdk_gc_set_foreground( osd_darea_gc , &bitmap_color );
+
+  gdk_draw_layout( GDK_DRAWABLE(osd_darea_bitmap) , osd_darea_gc ,
+                   off_x , off_y , osd_layout );
+  gdk_draw_layout( GDK_DRAWABLE(osd_darea_bitmap) , osd_darea_gc ,
+                   off_x + border_width , off_y , osd_layout );
+  gdk_draw_layout( GDK_DRAWABLE(osd_darea_bitmap) , osd_darea_gc ,
+                   off_x + border_width , off_y + border_width , osd_layout );
+  gdk_draw_layout( GDK_DRAWABLE(osd_darea_bitmap) , osd_darea_gc ,
+                   off_x , off_y + border_width , osd_layout );
+  gdk_draw_layout( GDK_DRAWABLE(osd_darea_bitmap) , osd_darea_gc ,
+                   off_x - border_width , off_y + border_width , osd_layout );
+  gdk_draw_layout( GDK_DRAWABLE(osd_darea_bitmap) , osd_darea_gc ,
+                   off_x - border_width , off_y , osd_layout );
+  gdk_draw_layout( GDK_DRAWABLE(osd_darea_bitmap) , osd_darea_gc ,
+                   off_x - border_width , off_y - border_width , osd_layout );
+  gdk_draw_layout( GDK_DRAWABLE(osd_darea_bitmap) , osd_darea_gc ,
+                   off_x , off_y - border_width , osd_layout );
+  gdk_draw_layout( GDK_DRAWABLE(osd_darea_bitmap) , osd_darea_gc ,
+                   off_x + border_width , off_y - border_width , osd_layout );
+
+  g_object_unref( osd_darea_gc );
+
+  gdk_window_set_back_pixmap( GDK_WINDOW(osd_darea->window) , osd_darea_pixmap , FALSE );
+  gdk_window_shape_combine_mask( GDK_WINDOW(osd_win->window) , osd_darea_bitmap , 0 , 0 );
+
+  gtk_widget_show( osd_win );
+
+  pango_font_description_free( osd_fontdesc );
+  g_object_unref( osd_layout );
+  return 0;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/aosd/aosd_ui.h	Mon Jan 29 06:40:04 2007 -0800
@@ -0,0 +1,32 @@
+/*
+*
+* Author: Giacomo Lozito <james@develia.org>, (C) 2005-2007
+*
+* This program is free software; you can redistribute it and/or modify it
+* under the terms of the GNU General Public License as published by the
+* Free Software Foundation; either version 2 of the License, or (at your
+* option) any later version.
+*
+* This program 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 this program; if not, write to the Free Software Foundation, Inc.,
+* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
+*
+*/
+
+#ifndef _I_AOSD_UI_H
+#define _I_AOSD_UI_H 1
+
+#include "aosd_common.h"
+#include "aosd_cfg.h"
+#include <glib.h>
+
+
+void aosd_ui_configure ( aosd_cfg_t * );
+void aosd_ui_about ( void );
+
+#endif /* !_I_AOSD_UI_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/aosd/ghosd-internal.h	Mon Jan 29 06:40:04 2007 -0800
@@ -0,0 +1,25 @@
+/* ghosd -- OSD with fake transparency, cairo, and pango.
+ * Copyright (C) 2006 Evan Martin <martine@danga.com>
+ */
+
+#include <X11/Xlib.h>
+
+#include "ghosd.h"
+
+typedef struct {
+  GhosdRenderFunc func;
+  void *data;
+  void (*data_destroy)(void*);
+} RenderCallback;
+
+struct _Ghosd {
+  Display *dpy;
+  Window win;
+  int transparent;
+  int x, y, width, height;
+  
+  Pixmap background;
+  RenderCallback render;
+};
+
+/* vim: set ts=2 sw=2 et cino=(0 : */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/aosd/ghosd-license	Mon Jan 29 06:40:04 2007 -0800
@@ -0,0 +1,20 @@
+Copyright (c) 2006 Evan Martin <martine@danga.com>
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/aosd/ghosd-main.c	Mon Jan 29 06:40:04 2007 -0800
@@ -0,0 +1,171 @@
+/* ghosd -- OSD with fake transparency, cairo, and pango.
+ * Copyright (C) 2006 Evan Martin <martine@danga.com>
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/time.h>
+#include <sys/poll.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "ghosd.h"
+#include "ghosd-internal.h"
+
+static void
+ghosd_main_iteration(Ghosd *ghosd) {
+  XEvent ev, pev;
+  XNextEvent(ghosd->dpy, &ev);
+  
+  /* smash multiple configure/exposes into one. */
+  if (ev.type == ConfigureNotify) {
+    while (XPending(ghosd->dpy)) {
+      XPeekEvent(ghosd->dpy, &pev);
+      if (pev.type != ConfigureNotify && pev.type != Expose)
+        break;
+      XNextEvent(ghosd->dpy, &ev);
+    }
+  }
+
+  switch (ev.type) {
+  case Expose:
+    break;
+  case ConfigureNotify:
+    if (ghosd->width > 0) {
+      /* XXX if the window manager disagrees with our positioning here,
+       * we loop. */
+      if (ghosd->x != ev.xconfigure.x ||
+          ghosd->y != ev.xconfigure.y) {
+        /*width = ev.xconfigure.width; 
+        height = ev.xconfigure.height;*/
+        XMoveResizeWindow(ghosd->dpy, ghosd->win,
+                          ghosd->x, ghosd->y, ghosd->width, ghosd->height);
+      }
+    }
+    break;
+  }
+}
+
+void
+ghosd_main_iterations(Ghosd *ghosd) {
+  while (XPending(ghosd->dpy))
+    ghosd_main_iteration(ghosd);
+}
+
+void
+ghosd_main_until(Ghosd *ghosd, struct timeval *until) {
+  struct timeval tv_now;
+
+  ghosd_main_iterations(ghosd);
+
+  for (;;) {
+    gettimeofday(&tv_now, NULL);
+    int dt = (until->tv_sec  - tv_now.tv_sec )*1000 +
+             (until->tv_usec - tv_now.tv_usec)/1000;
+    if (dt <= 0) break;
+
+    struct pollfd pollfd = { ghosd_get_socket(ghosd), POLLIN, 0 };
+    int ret = poll(&pollfd, 1, dt);
+    if (ret < 0) {
+      perror("poll");
+      exit(1);
+    } else if (ret > 0) {
+      ghosd_main_iterations(ghosd);
+    } else {
+      /* timer expired. */
+      break;
+    }
+  }
+}
+
+typedef struct {
+  cairo_surface_t* surface;
+  float alpha;
+  RenderCallback user_render;
+} GhosdFlashData;
+
+static void
+flash_render(Ghosd *ghosd, cairo_t *cr, void* data) {
+  GhosdFlashData *flash = data;
+
+  /* the first time we render, let the client render into their own surface. */
+  if (flash->surface == NULL) {
+    cairo_t *rendered_cr;
+    flash->surface = cairo_surface_create_similar(cairo_get_target(cr),
+                                                  CAIRO_CONTENT_COLOR_ALPHA,
+                                                  ghosd->width, ghosd->height);
+    rendered_cr = cairo_create(flash->surface);
+    flash->user_render.func(ghosd, rendered_cr, flash->user_render.data);
+    cairo_destroy(rendered_cr);
+  }
+
+  /* now that we have a rendered surface, all we normally do is copy that to
+   * the screen. */
+  cairo_set_source_surface(cr, flash->surface, 0, 0);
+  cairo_paint_with_alpha(cr, flash->alpha);
+}
+
+/* we don't need to free the flashdata object, because we stack-allocate that.
+ * but we do need to let the old user data free itself... */
+static void
+flash_destroy(void *data) {
+  GhosdFlashData *flash = data;
+  if (flash->user_render.data_destroy)
+    flash->user_render.data_destroy(flash->user_render.data);
+}
+
+void
+ghosd_flash(Ghosd *ghosd, int fade_ms, int total_display_ms) {
+  GhosdFlashData flash = {0};
+  memcpy(&flash.user_render, &ghosd->render, sizeof(RenderCallback));
+  ghosd_set_render(ghosd, flash_render, &flash, flash_destroy);
+
+  ghosd_show(ghosd);
+
+  const int STEP_MS = 50;
+  const float dalpha = 1.0 / (fade_ms / (float)STEP_MS);
+  struct timeval tv_nextupdate;
+
+  /* fade in. */
+  for (flash.alpha = 0; flash.alpha < 1.0; flash.alpha += dalpha) {
+    if (flash.alpha > 1.0) flash.alpha = 1.0;
+    ghosd_render(ghosd);
+
+    gettimeofday(&tv_nextupdate, NULL);
+    tv_nextupdate.tv_usec += STEP_MS*1000;
+    ghosd_main_until(ghosd, &tv_nextupdate);
+  }
+
+  /* full display. */
+  flash.alpha = 1.0;
+  ghosd_render(ghosd);
+
+  gettimeofday(&tv_nextupdate, NULL);
+  tv_nextupdate.tv_usec += (total_display_ms - (2*fade_ms))*1000;
+  ghosd_main_until(ghosd, &tv_nextupdate);
+
+  /* fade out. */
+  for (flash.alpha = 1.0; flash.alpha > 0.0; flash.alpha -= dalpha) {
+    ghosd_render(ghosd);
+
+    gettimeofday(&tv_nextupdate, NULL);
+    tv_nextupdate.tv_usec += STEP_MS*1000;
+    ghosd_main_until(ghosd, &tv_nextupdate);
+  }
+
+  flash.alpha = 0;
+  ghosd_render(ghosd);
+
+  /* display for another half-second,
+   * because otherwise the fade out attracts your eye
+   * and then you'll see a flash while it repaints where the ghosd was.
+   */
+  gettimeofday(&tv_nextupdate, NULL);
+  tv_nextupdate.tv_usec += 500*1000;
+  ghosd_main_until(ghosd, &tv_nextupdate);
+}
+
+/* vim: set ts=2 sw=2 et cino=(0 : */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/aosd/ghosd-text.c	Mon Jan 29 06:40:04 2007 -0800
@@ -0,0 +1,53 @@
+/* ghosd -- OSD with fake transparency, cairo, and pango.
+ * Copyright (C) 2006 Evan Martin <martine@danga.com>
+ */
+
+#include "config.h"
+
+#include <pango/pangocairo.h>
+#include "ghosd-internal.h"
+#include "ghosd-text.h"
+
+static void
+render_text(Ghosd *ghosd, cairo_t *cr, void* data) {
+  PangoLayout *layout = data;
+
+  /* drop shadow! */
+  cairo_set_source_rgba(cr, 0, 0, 0, 0.8);
+  cairo_move_to(cr, 4, 4);
+  pango_cairo_show_layout(cr, layout);
+
+  /* and the actual text. */
+  cairo_set_source_rgba(cr, 1, 1, 1, 1.0);
+  cairo_move_to(cr, 0, 0);
+  pango_cairo_show_layout(cr, layout);
+}
+
+Ghosd*
+ghosd_text_new(const char *markup, int x, int y) {
+  Ghosd *ghosd;
+  PangoContext *context;
+  PangoLayout *layout;
+  PangoRectangle ink_rect;
+
+  g_type_init();
+
+  context = pango_cairo_font_map_create_context(
+              PANGO_CAIRO_FONT_MAP(pango_cairo_font_map_get_default()));
+  layout = pango_layout_new(context);
+
+  pango_layout_set_markup(layout, markup, -1);
+
+  pango_layout_get_pixel_extents(layout, &ink_rect, NULL);
+
+  const int width = ink_rect.x + ink_rect.width+5;
+  const int height = ink_rect.y + ink_rect.height+5;
+
+  ghosd = ghosd_new();
+  ghosd_set_position(ghosd, x, y, width, height);
+  ghosd_set_render(ghosd, render_text, layout, g_object_unref);
+  
+  return ghosd;
+}
+
+/* vim: set ts=2 sw=2 et cino=(0 : */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/aosd/ghosd-text.h	Mon Jan 29 06:40:04 2007 -0800
@@ -0,0 +1,14 @@
+/* ghosd -- OSD with fake transparency, cairo, and pango.
+ * Copyright (C) 2006 Evan Martin <martine@danga.com>
+ */
+
+#ifndef __GHOSD_TEXT_H__
+#define __GHOSD_TEXT_H__
+
+#include <pango/pango-layout.h>
+
+Ghosd* ghosd_text_new(const char *markup, int x, int y);
+
+#endif /* __GHOSD_TEXT_H__ */
+
+/* vim: set ts=2 sw=2 et cino=(0 : */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/aosd/ghosd.c	Mon Jan 29 06:40:04 2007 -0800
@@ -0,0 +1,248 @@
+/* ghosd -- OSD with fake transparency, cairo, and pango.
+ * Copyright (C) 2006 Evan Martin <martine@danga.com>
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <cairo/cairo-xlib-xrender.h>
+#include <X11/Xatom.h>
+
+#include "ghosd.h"
+#include "ghosd-internal.h"
+
+static Pixmap
+take_snapshot(Ghosd *ghosd) {
+  Pixmap pixmap;
+  GC gc;
+
+  /* create a pixmap to hold the screenshot. */
+  pixmap = XCreatePixmap(ghosd->dpy, ghosd->win,
+                         ghosd->width, ghosd->height,
+                         DefaultDepth(ghosd->dpy, DefaultScreen(ghosd->dpy)));
+
+  /* then copy the screen into the pixmap. */
+  gc = XCreateGC(ghosd->dpy, pixmap, 0, NULL);
+  XSetSubwindowMode(ghosd->dpy, gc, IncludeInferiors);
+  XCopyArea(ghosd->dpy, DefaultRootWindow(ghosd->dpy), pixmap, gc,
+            ghosd->x, ghosd->y, ghosd->width, ghosd->height,
+            0, 0);
+  XSetSubwindowMode(ghosd->dpy, gc, ClipByChildren);
+  XFreeGC(ghosd->dpy, gc);
+
+  return pixmap;
+}
+
+void
+ghosd_render(Ghosd *ghosd) {
+  Pixmap pixmap;
+  GC gc;
+
+  /* make our own copy of the background pixmap as the initial surface. */
+  pixmap = XCreatePixmap(ghosd->dpy, ghosd->win, ghosd->width, ghosd->height,
+                         DefaultDepth(ghosd->dpy, DefaultScreen(ghosd->dpy)));
+
+  gc = XCreateGC(ghosd->dpy, pixmap, 0, NULL);
+  if (ghosd->transparent) {
+    XCopyArea(ghosd->dpy, ghosd->background, pixmap, gc,
+              0, 0, ghosd->width, ghosd->height, 0, 0);
+  } else {
+    XFillRectangle(ghosd->dpy, pixmap, gc,
+                  0, 0, ghosd->width, ghosd->height);
+  }
+  XFreeGC(ghosd->dpy, gc);
+
+  /* render with cairo. */
+  if (ghosd->render.func) {
+    /* create cairo surface using the pixmap. */
+    XRenderPictFormat *xrformat = 
+      XRenderFindVisualFormat(ghosd->dpy,
+                              DefaultVisual(ghosd->dpy,
+                                            DefaultScreen(ghosd->dpy)));
+    cairo_surface_t *surf =
+      cairo_xlib_surface_create_with_xrender_format(
+        ghosd->dpy, pixmap,
+        ScreenOfDisplay(ghosd->dpy, DefaultScreen(ghosd->dpy)),
+        xrformat,
+        ghosd->width, ghosd->height);
+
+    /* draw some stuff. */
+    cairo_t *cr = cairo_create(surf);
+    ghosd->render.func(ghosd, cr, ghosd->render.data);
+    cairo_destroy(cr);
+  }
+
+  /* point window at its new backing pixmap. */
+  XSetWindowBackgroundPixmap(ghosd->dpy, ghosd->win, pixmap);
+  /* I think it's ok to free it here because XCreatePixmap(3X11) says: "the X
+   * server frees the pixmap storage when there are no references to it".
+   */
+  XFreePixmap(ghosd->dpy, pixmap);
+
+  /* and tell the window to redraw with this pixmap. */
+  XClearWindow(ghosd->dpy, ghosd->win);
+}
+
+static void
+set_hints(Display *dpy, Window win) {
+  /* we're almost a _NET_WM_WINDOW_TYPE_SPLASH, but we don't want
+   * to be centered on the screen.  instead, manually request the
+   * behavior we want. */
+
+  /* turn off window decorations.
+   * we could pull this in from a motif header, but it's easier to
+   * use this snippet i found on a mailing list.  */
+  Atom mwm_hints = XInternAtom(dpy, "_MOTIF_WM_HINTS", False);
+#define MWM_HINTS_DECORATIONS (1<<1)
+  struct {
+    long flags, functions, decorations, input_mode;
+  } mwm_hints_setting = {
+    MWM_HINTS_DECORATIONS, 0, 0, 0
+  };
+  XChangeProperty(dpy, win,
+    mwm_hints, mwm_hints, 32, PropModeReplace,
+    (unsigned char *)&mwm_hints_setting, 4);
+
+  /* always on top, not in taskbar or pager. */
+  Atom win_state = XInternAtom(dpy, "_NET_WM_STATE", False);
+  Atom win_state_setting[] = {
+    XInternAtom(dpy, "_NET_WM_STATE_ABOVE", False),
+    XInternAtom(dpy, "_NET_WM_STATE_SKIP_TASKBAR", False),
+    XInternAtom(dpy, "_NET_WM_STATE_SKIP_PAGER", False)
+  };
+  XChangeProperty(dpy, win, win_state, XA_ATOM, 32,
+                  PropModeReplace, (unsigned char*)&win_state_setting, 3);
+}
+
+static Window
+make_window(Display *dpy) {
+  Window win;
+  XSetWindowAttributes att;
+
+  /* XXX I don't understand X well enough to know if these are the correct
+   * settings. */
+  att.backing_store = WhenMapped;
+  att.background_pixel = None;
+  att.border_pixel = 0;
+  att.background_pixmap = None;
+  att.save_under = True;
+  att.event_mask = ExposureMask | StructureNotifyMask;
+  att.override_redirect = True;
+
+  win = XCreateWindow(dpy, DefaultRootWindow(dpy),
+                      -1, -1, 1, 1, 0,
+                      CopyFromParent, InputOutput, CopyFromParent,
+                      CWBackingStore | CWBackPixel | CWBackPixmap |
+                      CWEventMask | CWSaveUnder | CWOverrideRedirect,
+                      &att);
+
+  set_hints(dpy, win);
+
+  /* XXX: XSetClassHint? */
+
+  return win;
+}
+
+void
+ghosd_show(Ghosd *ghosd) {
+  if (ghosd->transparent) {
+    if (ghosd->background)
+      XFreePixmap(ghosd->dpy, ghosd->background);
+    ghosd->background = take_snapshot(ghosd);
+  }
+
+  ghosd_render(ghosd);
+
+  XMapWindow(ghosd->dpy, ghosd->win);
+}
+
+void
+ghosd_hide(Ghosd *ghosd) {
+  XUnmapWindow(ghosd->dpy, ghosd->win);
+}
+
+void
+ghosd_set_transparent(Ghosd *ghosd, int transparent) {
+  ghosd->transparent = (transparent != 0);
+}
+
+void
+ghosd_set_render(Ghosd *ghosd, GhosdRenderFunc render_func,
+                 void *user_data, void (*user_data_d)(void*)) {
+  ghosd->render.func = render_func;
+  ghosd->render.data = user_data;
+  ghosd->render.data_destroy = user_data_d;
+}
+
+void
+ghosd_set_position(Ghosd *ghosd, int x, int y, int width, int height) {
+  const int dpy_width  = DisplayWidth(ghosd->dpy,  DefaultScreen(ghosd->dpy));
+  const int dpy_height = DisplayHeight(ghosd->dpy, DefaultScreen(ghosd->dpy));
+
+  if (x == GHOSD_COORD_CENTER) {
+    x = (dpy_width - width) / 2;
+  } else if (x < 0) {
+    x = dpy_width - width + x;
+  }
+
+  if (y == GHOSD_COORD_CENTER) {
+    y = (dpy_height - height) / 2;
+  } else if (y < 0) {
+    y = dpy_height - height + y;
+  }
+
+  ghosd->x      = x;
+  ghosd->y      = y;
+  ghosd->width  = width;
+  ghosd->height = height;
+
+  XMoveResizeWindow(ghosd->dpy, ghosd->win,
+                    ghosd->x, ghosd->y, ghosd->width, ghosd->height);
+}
+
+#if 0
+static int
+x_error_handler(Display *dpy, XErrorEvent* evt) {
+  /* segfault so we can get a backtrace. */
+  char *x = NULL;
+  *x = 0;
+  return 0;
+}
+#endif
+
+Ghosd*
+ghosd_new(void) {
+  Ghosd *ghosd;
+  Display *dpy;
+  Window win;
+
+  dpy = XOpenDisplay(NULL);
+  if (dpy == NULL) {
+    fprintf(stderr, "Couldn't open display: (XXX FIXME)\n");
+    return NULL;
+  }
+
+  win = make_window(dpy);
+  
+  ghosd = calloc(1, sizeof(Ghosd));
+  ghosd->dpy = dpy;
+  ghosd->win = win;
+  ghosd->transparent = 1;
+
+  return ghosd;
+}
+
+void
+ghosd_destroy(Ghosd* ghosd) {
+  if (ghosd->background)
+    XFreePixmap(ghosd->dpy, ghosd->background);
+  XDestroyWindow(ghosd->dpy, ghosd->win);
+}
+
+int
+ghosd_get_socket(Ghosd *ghosd) {
+  return ConnectionNumber(ghosd->dpy);
+}
+
+/* vim: set ts=2 sw=2 et cino=(0 : */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/aosd/ghosd.h	Mon Jan 29 06:40:04 2007 -0800
@@ -0,0 +1,38 @@
+/* ghosd -- OSD with fake transparency, cairo, and pango.
+ * Copyright (C) 2006 Evan Martin <martine@danga.com>
+ */
+
+#ifndef __GHOSD_H__
+#define __GHOSD_H__
+
+#include <cairo/cairo.h>
+
+#include <values.h>  /* MAXINT */
+#include <sys/time.h>  /* timeval */
+
+typedef struct _Ghosd Ghosd;
+
+typedef void (*GhosdRenderFunc)(Ghosd *ghosd, cairo_t *cr, void *user_data);
+
+Ghosd *ghosd_new(void);
+void   ghosd_destroy(Ghosd* ghosd);
+
+#define GHOSD_COORD_CENTER MAXINT
+void ghosd_set_transparent(Ghosd *ghosd, int transparent);
+void ghosd_set_position(Ghosd *ghosd, int x, int y, int width, int height);
+void ghosd_set_render(Ghosd *ghosd, GhosdRenderFunc render_func,
+                      void* user_data, void (*user_data_d)(void*));
+
+void ghosd_render(Ghosd *ghosd);
+void ghosd_show(Ghosd *ghosd);
+void ghosd_hide(Ghosd *ghosd);
+
+void ghosd_main_iterations(Ghosd *ghosd);
+void ghosd_main_until(Ghosd *ghosd, struct timeval *until);
+void ghosd_flash(Ghosd *ghosd, int fade_ms, int total_display_ms);
+
+int ghosd_get_socket(Ghosd *ghosd);
+
+#endif /* __GHOSD_H__ */
+
+/* vim: set ts=2 sw=2 et cino=(0 : */