/*
 * gkrellfire
 * ----------
 *
 */

#if defined(WIN32)
#include <src/gkrellm.h>
#include <src/win32-plugin.h>
#else
#include <gkrellm2/gkrellm.h>
#endif

// Make sure we have a compatible version of GKrellM (Version 2.0+ is required)
#if !defined(GKRELLM_VERSION_MAJOR) || (GKRELLM_VERSION_MAJOR < 2)
#error This plugin requires GKrellM version >= 2.0
#endif

#define VERSION_GKRELLFIRE	"0.4.1"

#define	CONFIG_NAME		"GkrellFire"
#define	STYLE_NAME		"GkrellFire"

#define PLUGIN_PLACEMENT	MON_CPU
#define CHART_HEIGHT		64
#define CHART_MAX_WIDTH		120

static GkrellmMonitor		*mon;
static GkrellmChart		*chart;
static GkrellmChartconfig	*chart_config = NULL;

static GtkWidget		*style_radio[4];
static GtkWidget		*bg_radio[2];
static GtkWidget		*color_button;
static GtkWidget		*bgcolor_button;
static GtkWidget		*monitor_check;
static GkrellmPiximage		*piximage = NULL;
static GtkWidget		*colorseldlg = NULL;
static GtkWidget		*bgcolorseldlg = NULL;
static GdkColor			fcolor;
static GdkColor			bgcolor;

static gint			flame_mode = 1;
static gint			flame_style = 0;
static gint			flame_color;
static gint			bg_color;
static gint			bg_transparent = 0;

static gint			chart_width = 0;
static gint			style_id;
static gint			cpu_load;

static guchar			rgbbuf[CHART_MAX_WIDTH * CHART_HEIGHT * 3];
static guchar			firebuffer[CHART_MAX_WIDTH * CHART_HEIGHT];
static guchar			xpm_buffer[64000];
static guchar			*xpm[512];
static gint			ftab[] = {25,15,8,7,6,5,4,3,3,2,2};

static struct
{
 guchar red;
 guchar green;
 guchar blue;
} rgb[256];


static void calc_palette(int start_index, int end_index, long start_color, long end_color)
{
 int cnt, ind;
 float s_red, s_green, s_blue, e_red, e_green, e_blue;
 float d_red, d_green, d_blue;

 s_red = (start_color & 0xff0000) >> 16;
 s_green = (start_color & 0xff00) >> 8;
 s_blue = start_color & 0xff;

 e_red = (end_color & 0xff0000) >> 16;
 e_green = (end_color & 0xff00) >> 8;
 e_blue = end_color & 0xff;

 cnt = end_index - start_index;
 d_red = (e_red - s_red) / cnt;
 d_green = (e_green - s_green) / cnt;
 d_blue = (e_blue - s_blue) / cnt;

 for ( ind = 1; ind < cnt; ind++)
 {
  rgb[start_index + ind].red = s_red + d_red * ind;
  rgb[start_index + ind].green = s_green + d_green * ind;
  rgb[start_index + ind].blue = s_blue + d_blue * ind;
 }

 rgb[start_index].red = s_red;
 rgb[start_index].green = s_green;
 rgb[start_index].blue = s_blue;
 rgb[end_index].red = e_red;
 rgb[end_index].green = e_green;
 rgb[end_index].blue = e_blue;
}

static void set_palette(int mode, int color, int bcolor)
{
 if (bg_transparent)
 {
  switch (mode)
  {
   case 0 : calc_palette(0, 85, 0x000055, 0xff0000);
            calc_palette(85, 170, 0xff0000, 0xffff00);
            calc_palette(170, 255, 0xffff00, 0xffffff);
            break;
   case 1 : calc_palette(0, 85, 0x333333, 0xff0000);
            calc_palette(85, 170, 0xff0000, 0xffff00);
            calc_palette(170, 255, 0xffff00, 0xffffff);
            break;
   case 2 : calc_palette(0, 85, 0x550000, 0xff0000);
            calc_palette(85, 170, 0xff0000, 0xffff00);
            calc_palette(170, 255, 0xffff00, 0xffffff);
            break;
   case 3 : calc_palette(0, 255, 0x222222, color);
            break;
  }
 }
 else
 {
  switch (mode)
  {
   case 0 : calc_palette(1, 85, 0x000055, 0xff0000);
            calc_palette(85, 170, 0xff0000, 0xffff00);
            calc_palette(170, 255, 0xffff00, 0xffffff);
            break;
   case 1 : calc_palette(1, 85, 0x333333, 0xff0000);
            calc_palette(85, 170, 0xff0000, 0xffff00);
            calc_palette(170, 255, 0xffff00, 0xffffff);
            break;
   case 2 : calc_palette(1, 85, 0x550000, 0xff0000);
            calc_palette(85, 170, 0xff0000, 0xffff00);
            calc_palette(170, 255, 0xffff00, 0xffffff);
            break;
   case 3 : calc_palette(1, 255, 0x222222, color);
            break;
  }

  rgb[0].red = (bcolor & 0xff0000) >> 16;
  rgb[0].green = (bcolor & 0xff00) >> 8;
  rgb[0].blue = bcolor & 0xff;
 }
}

static void bottom_line(unsigned char *buffer)
{
 int x, y;
 unsigned char c;

 y = (chart_width * (CHART_HEIGHT - 1));

 for (x = 0; x < chart_width ; x++)
 {
  if (rand() % 10 < 5)
    c = 255;
  else
    c = 0;

  buffer[y + x] = c;
 }
}

static void fire(int fire_height)
{
 int x, y, yo, xo, r, c;

 for (y = CHART_HEIGHT - 1; y > 0; y--)
 {
  yo = y * chart_width;
  for (x = 0; x < chart_width; x++)
  {
   c = ((firebuffer[yo + x - 1] + firebuffer[yo + x] + firebuffer[yo + x + 1]) / 3);

   r = rand() % 30;

   if (r < 5)
   {
     xo = 1;
     c -= fire_height;
   }
   else
   {
    xo = 0;
    c -= fire_height;
   }

   if (c < 0)
     c = 0;

   firebuffer[(yo - chart_width) + x + xo] = c;
  }
 }

 yo = chart_width * (CHART_HEIGHT - 1);

 r = rand() % 10;

 if (r < 5)
   c = 0;
 else
   c = 255;

 firebuffer[yo + (rand() % chart_width)] = c;
}

static gint calc_cpu_load(void)
{
 static int prev_load = 0;
 static int first_time = 1;

 int load, r, fp;
 int cpu, nice, system, idle;
 char b[1024];

 fp = open("/proc/stat", O_RDONLY);
 r = read(fp, b, sizeof(b));
 close(fp);

 if (r < 0)
   return;

 sscanf(b, "%*s %d %d %d %d", &cpu, &nice, &system, &idle);

 load = cpu + nice + system;

 if (first_time)
 {
  prev_load = load;
  first_time = 0;
 }

 cpu_load = load - prev_load;
 prev_load = load;

 if (cpu_load < 0)
   cpu_load = 0;
 else
   if (cpu_load > 100)
     cpu_load = 100;

 return TRUE;
}

char get_hb_a(int c, int h)
{
 if (h)
 {
  c &= 0xf0;
  c /= 16;
 }
 else
   c &= 0x0f;

 if (c > 9)
 {
  c -= 10;
  c += 'A';
 }
 else
   c += '0';

 return(c);
}

static void gen_xpm(void)
{
 int n, x, y, i;
 char *p, *f;

 i = 0;
 p = xpm_buffer;
 f = firebuffer;

 xpm[i++] = p;
 p += sprintf(p ,"%d %d 256 2", chart_width, CHART_HEIGHT) + 1;

 xpm[i++] = p;
 if (bg_transparent)
 {
  strcpy(p, "00 c None");
  p += 10;
 }
 else
   p += sprintf(p, "00 c #%02X%02X%02X", rgb[0].red, rgb[0].green, rgb[0].blue) + 1;

 for (n = 1; n <= 255; n++)
 {
  xpm[i++] = p;
  *p++ = get_hb_a(n, 1);
  *p++ = get_hb_a(n, 0);
  *p++ = ' ';
  *p++ = 'c';
  *p++ = ' ';
  *p++ = '#';
  *p++ = get_hb_a(rgb[n].red, 1);
  *p++ = get_hb_a(rgb[n].red, 0);
  *p++ = get_hb_a(rgb[n].green, 1);
  *p++ = get_hb_a(rgb[n].green, 0);
  *p++ = get_hb_a(rgb[n].blue, 1);
  *p++ = get_hb_a(rgb[n].blue, 0);
  *p++ = 0;
 }

 for (y = 0; y < CHART_HEIGHT; y++)
 {
  xpm[i++] = p;
  for (x = 0; x < chart_width; x++)
  {
   *p++ = get_hb_a(*f, 1);
   *p++ = get_hb_a(*f++, 0);
  }
  *p++ = 0;
 }

 xpm[i++] = p;
 *p++ = 0;
}

static gint chart_expose_event(GtkWidget *widget, GdkEventExpose *event)
{
 if (bg_transparent)
 {
  gkrellm_draw_chart_to_screen(chart);
  gkrellm_paste_piximage(piximage, widget->window, 0 , 0, chart_width, CHART_HEIGHT);
 }
 else
   gdk_draw_rgb_image (widget->window, widget->style->fg_gc[GTK_STATE_NORMAL], 0, 0, chart_width, CHART_HEIGHT,
                       GDK_RGB_DITHER_MAX, rgbbuf, chart_width * 3);

 return TRUE;
}

static gint timer_callback(GtkWidget *widget)
{
 GdkEventExpose event;
 gint ret_val;

 guchar *pos;
 gint x, y, c;

 if (flame_mode)
   fire(ftab[cpu_load / 10]);
 else
   fire(2);

 if (bg_transparent)
 {
  gen_xpm();

  if (piximage)
    gkrellm_destroy_piximage(piximage);

  piximage = gkrellm_piximage_new_from_xpm_data((gchar **) xpm);
 }
 else
 {
  pos = rgbbuf;

  for (y = 0; y < (CHART_HEIGHT); y++)
  {
   for (x = 0; x < chart_width; x++)
   {
    c = firebuffer[y * chart_width + x];

    *pos++ = rgb[c].red;
    *pos++ = rgb[c].green;
    *pos++ = rgb[c].blue;
   }
  }
 }

 gtk_signal_emit_by_name(GTK_OBJECT (chart->drawing_area), "expose_event", &event, &ret_val);

 return TRUE;
}

static gint key_press(GtkWidget *widget, GdkEventButton *event)
{
 if (event->button == 1)
 {
  flame_style++;

  if (flame_style > 3)
    flame_style = 0;

  set_palette(flame_style, flame_color, bg_color);
 }

 if (event->button == 2)
 {
  bg_transparent ^= 1;
  set_palette(flame_style, flame_color, bg_color);
 }

 if (event->button == 3)
 {
  gkrellm_open_config_window(mon);
 }

 return TRUE;
}

static void set_color()
{
 flame_color = 0;
 flame_color += (fcolor.red / 256) << 16;
 flame_color += (fcolor.green / 256) << 8;
 flame_color += fcolor.blue / 256;
 
 bg_color = 0;
 bg_color += (bgcolor.red / 256) << 16;
 bg_color += (bgcolor.green / 256) << 8;
 bg_color += bgcolor.blue / 256;
  
 set_palette(flame_style, flame_color, bg_color);
}

static void color_changed(GtkWidget *widget, GtkColorSelection *colorsel)
{
 gtk_color_selection_get_current_color(colorsel, &fcolor);

 set_color();
 set_palette(flame_style, flame_color, bg_color); 
}

static void color_func(GtkWidget *widget, gpointer data)
{
 gint res;
 GtkColorSelection *colorsel;
 GdkColor oldflamecol;

 if (colorseldlg == NULL)
   colorseldlg = gtk_color_selection_dialog_new("Select flame color");

 colorsel = GTK_COLOR_SELECTION (GTK_COLOR_SELECTION_DIALOG (colorseldlg)->colorsel);

 gtk_color_selection_set_previous_color(colorsel, &fcolor);
 gtk_color_selection_set_current_color(colorsel, &fcolor);
 gtk_color_selection_set_has_palette(colorsel, TRUE);

 g_signal_connect(G_OBJECT (colorsel), "color_changed", G_CALLBACK (color_changed), (gpointer) colorsel);

 oldflamecol = fcolor;
 
 res = gtk_dialog_run(GTK_DIALOG (colorseldlg));

 if (res == GTK_RESPONSE_OK)
 {
  gtk_color_selection_get_current_color(colorsel, &fcolor);
  
  set_color();
  set_palette(flame_style, flame_color, bg_color);
 }
 else
 {
  fcolor = oldflamecol;
  
  set_color();
  set_palette(flame_style, flame_color, bg_color);
 }
    
 gtk_widget_hide(colorseldlg);
}

static void bgcolor_changed(GtkWidget *widget, GtkColorSelection *colorsel)
{
 gtk_color_selection_get_current_color(colorsel, &bgcolor);

 set_color(); 
 set_palette(flame_style, flame_color, bg_color); 
}

static void bgcolor_func(GtkWidget *widget, gpointer data)
{
 gint res;
 GtkColorSelection *colorsel;
 GdkColor oldbgcol;

 if (bgcolorseldlg == NULL)
   bgcolorseldlg = gtk_color_selection_dialog_new("Select background color");

 colorsel = GTK_COLOR_SELECTION (GTK_COLOR_SELECTION_DIALOG (bgcolorseldlg)->colorsel);

 gtk_color_selection_set_previous_color(colorsel, &bgcolor);
 gtk_color_selection_set_current_color(colorsel, &bgcolor);
 gtk_color_selection_set_has_palette(colorsel, TRUE);

 g_signal_connect(G_OBJECT (colorsel), "color_changed", G_CALLBACK (bgcolor_changed), (gpointer) colorsel);

 oldbgcol = bgcolor;
 
 res = gtk_dialog_run(GTK_DIALOG (bgcolorseldlg));

 if (res == GTK_RESPONSE_OK)
 {
  gtk_color_selection_get_current_color(colorsel, &bgcolor);
  
  set_color(); 
  set_palette(flame_style, flame_color, bg_color);
 }
 else
 {
  bgcolor = oldbgcol;

  set_color(); 
  set_palette(flame_style, flame_color, bg_color);
 }
    
 gtk_widget_hide(bgcolorseldlg); 
}

static void create_plugin_tab(GtkWidget *tab_vbox)
{
 GtkWidget	*tabs;
 GtkWidget	*vbox, *vbox1;
 GtkWidget      *frame;
 GtkWidget	*text;
 GtkWidget	*label;
 GSList		*group = NULL, *group1 = NULL;
 gchar		buf[256];
 gint n;

 tabs = gtk_notebook_new();
 gtk_notebook_set_tab_pos(GTK_NOTEBOOK (tabs), GTK_POS_TOP);
 gtk_box_pack_start(GTK_BOX (tab_vbox), tabs, TRUE, TRUE, 0);

 vbox = gkrellm_gtk_framed_notebook_page(tabs, "Setup");

 frame = gtk_frame_new("General");
 gtk_box_pack_start(GTK_BOX (vbox), frame, TRUE, TRUE, 0);

 monitor_check = gtk_check_button_new_with_label("monitor system load");
 if (flame_mode)
   gtk_toggle_button_set_active((GtkToggleButton *) monitor_check, 1);

 vbox1 = gtk_vbox_new(FALSE, 2);
 gtk_container_add(GTK_CONTAINER (frame), vbox1);
 gtk_box_pack_start(GTK_BOX (vbox1), monitor_check, TRUE, TRUE, 2);

 frame = gtk_frame_new("Flame");
 gtk_box_pack_start(GTK_BOX (vbox), frame, TRUE, TRUE, 2);
 vbox1 = gtk_vbox_new(FALSE, 2);
 gtk_container_add(GTK_CONTAINER (frame), vbox1);

 style_radio[0] = gtk_radio_button_new_with_label(group, "Style 1");
 style_radio[1] = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON (style_radio[0]), "Style 2");
 style_radio[2] = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON (style_radio[0]), "Style 3");
 style_radio[3] = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON (style_radio[0]), "Style 4");

 color_button = gtk_button_new_with_label("choose flame color ...");
 g_signal_connect(G_OBJECT (color_button), "clicked", G_CALLBACK (color_func), (gpointer) NULL);
 
 gtk_toggle_button_set_active((GtkToggleButton *) style_radio[flame_style], 1);

 for (n = 0; n < 4; n++)
   gtk_box_pack_start(GTK_BOX (vbox1), style_radio[n], TRUE, TRUE, 2);

 gtk_box_pack_start(GTK_BOX (vbox1), color_button, TRUE, TRUE, 2);

 frame = gtk_frame_new("Background");
 gtk_box_pack_start(GTK_BOX (vbox), frame, TRUE, TRUE, 2);
 vbox1 = gtk_vbox_new(FALSE, 2);
 gtk_container_add(GTK_CONTAINER (frame), vbox1);

 bg_radio[0] = gtk_radio_button_new_with_label(group1, "Transparency");
 bg_radio[1] = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON (bg_radio[0]), "Color");

 bgcolor_button = gtk_button_new_with_label("choose background color ...");
 g_signal_connect(G_OBJECT (bgcolor_button), "clicked", G_CALLBACK (bgcolor_func), (gpointer) NULL);
 
 if (bg_transparent)
   gtk_toggle_button_set_active((GtkToggleButton *) bg_radio[0], 1);
 else
   gtk_toggle_button_set_active((GtkToggleButton *) bg_radio[1], 1);

 gtk_box_pack_start(GTK_BOX (vbox1), bg_radio[0], TRUE, TRUE, 2);
 gtk_box_pack_start(GTK_BOX (vbox1), bg_radio[1], TRUE, TRUE, 2);
 gtk_box_pack_start(GTK_BOX (vbox1), bgcolor_button, TRUE, TRUE, 2);

 sprintf(buf, "GKrellFire %s\nGKrellM System Load Monitor\n\n(C)2003 Thomas Steinke\n"
	      "T.Steinke@web.de\n"
              "http://people.freenet.de/thomas-steinke\n\n"
              "Released under the GNU General Public License\n", VERSION_GKRELLFIRE);

 label = gtk_label_new("About");
 text = gtk_label_new(buf);
 gtk_notebook_append_page(GTK_NOTEBOOK(tabs),text,label);
}

static void apply_plugin()
{
 gint n;

 if (gtk_toggle_button_get_active((GtkToggleButton *) monitor_check))
   flame_mode = 1;
 else
   flame_mode = 0;

 if (gtk_toggle_button_get_active((GtkToggleButton *) bg_radio[0]))
   bg_transparent = 1;
 else
   bg_transparent = 0;

 for (n = 0; n < 4; n++)
   if (gtk_toggle_button_get_active((GtkToggleButton *) style_radio[n]))
     flame_style = n;

 set_palette(flame_style, flame_color, bg_color);
}

static void fire_save_config(FILE *file)
{
 fprintf(file, "%s style %d\n", CONFIG_NAME, flame_style);
 fprintf(file, "%s mode %d\n", CONFIG_NAME, flame_mode);
 fprintf(file, "%s color.red %d\n", CONFIG_NAME, fcolor.red);
 fprintf(file, "%s color.green %d\n", CONFIG_NAME, fcolor.green);
 fprintf(file, "%s color.blue %d\n", CONFIG_NAME, fcolor.blue);
 fprintf(file, "%s bgcolor.red %d\n", CONFIG_NAME, bgcolor.red);
 fprintf(file, "%s bgcolor.green %d\n", CONFIG_NAME, bgcolor.green);
 fprintf(file, "%s bgcolor.blue %d\n", CONFIG_NAME, bgcolor.blue);
 fprintf(file, "%s transparent %d\n", CONFIG_NAME, bg_transparent);
}

static void fire_load_config(gchar *arg)
{
 gchar s[32];
 gint i;

 sscanf(arg, "%s %d", s, &i);

 if (!strcmp(s, "style"))
   flame_style = i;
 else if (!strcmp(s, "mode"))
        flame_mode = i;
 else if (!strcmp(s, "color.red"))
        fcolor.red = i;
 else if (!strcmp(s, "color.green"))
        fcolor.green = i;
 else if (!strcmp(s, "color.blue"))
        fcolor.blue = i;
 else if (!strcmp(s, "bgcolor.red"))
        bgcolor.red = i;
 else if (!strcmp(s, "bgcolor.green"))
        bgcolor.green = i;
 else if (!strcmp(s, "bgcolor.blue"))
        bgcolor.blue = i;
 else if (!strcmp(s, "transparent"))
        bg_transparent = i;
 
 set_color();
 set_palette(flame_style, flame_color, bg_color);
}

static void update_plugin()
{
}

static void create_plugin(GtkWidget *vbox, gint first_create)
{
 GkrellmStyle *style;
 gint cw;

 if (first_create)
 {
  chart = gkrellm_chart_new0();
 }

 gkrellm_set_chart_height_default(chart, CHART_HEIGHT);
 gkrellm_chart_create(vbox, mon, chart, &chart_config);

 style = gkrellm_meter_style(style_id);

 cw = gkrellm_chart_width();

 if (chart_width != cw)
 {
  chart_width = cw;

  if (chart_width > CHART_MAX_WIDTH)
    chart_width = CHART_MAX_WIDTH;

  bottom_line(firebuffer);
 }

 if (first_create)
 {
  gtk_signal_connect(GTK_OBJECT (chart->drawing_area), "expose_event", (GtkSignalFunc) chart_expose_event, NULL);
  gtk_signal_connect(GTK_OBJECT (chart->drawing_area), "button_press_event", (GtkSignalFunc) key_press, NULL);
  gtk_timeout_add (60, (GtkFunction) timer_callback, GTK_WIDGET (chart->drawing_area));
  gtk_timeout_add (1000, (GtkFunction) calc_cpu_load, NULL);
 }
}

static GkrellmMonitor plugin_mon =
{
 CONFIG_NAME,        	/* Name, for config tab.    */
 0,			/* Id,  0 if a plugin       */
 create_plugin,		/* The create function      */
 update_plugin,		/* The update function      */
 create_plugin_tab,	/* The config tab create function   */
 apply_plugin,		/* Apply the config function        */

 fire_save_config,	/* Save user config*/
 fire_load_config,	/* Load user config*/
 CONFIG_NAME,		/* config keyword*/

 NULL,			/* Undefined 2	*/
 NULL,			/* Undefined 1	*/
 NULL,			/* Undefined 0	*/

 PLUGIN_PLACEMENT,	/* Insert plugin before this monitor*/
 NULL,			/* Handle if a plugin, filled in by GKrellM */
 NULL			/* path if a plugin, filled in by GKrellM   */
};

// All GKrellM plugins must have one global routine named init_plugin()
// which returns a pointer to a filled in monitor structure.

#if defined(WIN32)
__declspec(dllexport) GkrellmMonitor *gkrellm_init_plugin(win32_plugin_callbacks* calls)
#else
GkrellmMonitor *gkrellm_init_plugin()
#endif
{
#if defined(WIN32)
    callbacks = calls;
    pwin32GK = calls->GK;
#endif

 style_id = gkrellm_add_meter_style(&plugin_mon, STYLE_NAME);

 chart_width = gkrellm_chart_width();
 if (chart_width > CHART_MAX_WIDTH)
   chart_width = CHART_MAX_WIDTH;
   
 fcolor.red = 0;
 fcolor.green = 65535;
 fcolor.blue = 0;
 
 bgcolor.red = 0;
 bgcolor.green = 0;
 bgcolor.blue = 13056;
 
 set_color();

 srand(time(NULL));
 bottom_line(firebuffer);

 mon = &plugin_mon;

 return (&plugin_mon);
}


syntax highlighted by Code2HTML, v. 0.9.1