Kaynağa Gözat

Initial commit

Jindrich Makovicka 7 yıl önce
işleme
19daab07f3
4 değiştirilmiş dosya ile 957 ekleme ve 0 silme
  1. 30 0
      Makefile
  2. 68 0
      README
  3. 21 0
      fm_led.xpm
  4. 838 0
      fmonitor.c

+ 30 - 0
Makefile

@@ -0,0 +1,30 @@
+VERSION = 2.0.4
+PKGNAME = gkrellm-fmonitor
+
+CFLAGS = -O2 -Wall -fPIC
+CC = gcc
+
+SRCS = fmonitor.c
+OBJS = fmonitor.o
+SOBJ = fmonitor.so
+
+
+$(SOBJ): $(OBJS)
+	$(CC) -shared `pkg-config gtk+-2.0 --libs` $(OBJS) -o $(SOBJ)
+
+install:
+	cp $(SOBJ) /usr/local/lib/gkrellm2/plugins
+
+clean:
+	rm -f *.o core *.so* *.bak *~
+
+
+fmonitor.o: fmonitor.c
+	$(CC) $(CFLAGS) `pkg-config gtk+-2.0 --cflags` -c $(SRCS) -o $(OBJS)
+
+dist:
+	rm -rf $(PKGNAME)-$(VERSION)
+	mkdir $(PKGNAME)-$(VERSION)
+	cp Makefile README fmonitor.c fm_led.xpm $(PKGNAME)-$(VERSION)/
+	tar zcf $(PKGNAME)-$(VERSION).tgz $(PKGNAME)-$(VERSION)
+	rm -rf $(PKGNAME)-$(VERSION)

+ 68 - 0
README

@@ -0,0 +1,68 @@
+------------------------------------------------------------------------------
+ File Monitor plugin for GKrellM
+ Copyright (C) 2001 Anatoly Asviyan
+ aanatoly@linuxmail.org
+
+ This program is free software which I release under the GNU General Public
+ License. You may redistribute and/or modify this program under the terms
+ of that 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.
+
+ To get a copy of the GNU General Puplic License,  write to the
+ Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+------------------------------------------------------------------------------
+
+This plugin monitors a file and displays its contens in gkrellm. File can have
+multiple rows of the form 'name : value : [flag]'. If flag non-empty then
+for WARNING value - the orange led will light up and for ALERT value - red.
+For example
+  CPU:50.8:ALERT
+  SBr:33.4:WARNING
+  Fan1:4560:
+or
+  Temp:31 C:
+  Hum:49 %:
+
+Plugin can monitor multiple files and for each file, you can specify
+the following:
+Label           - label of gkrellm panel
+File to monitor - as is :) - file to monitor. It would be checked every 5 sec
+Update program  - the program to update a monitored file.
+Warning command - any shell command to run whenever warning flag is set.
+Alert command   - any shell command to run whenever alert flag is set.
+
+I'm using it to monitor my HW state from via686a sensor chip. For this purpose
+I wrote sensors.tcl script that periodicaly run 'sensors' utility, processes
+its output and writes results to ~/.sensors in the above format. 
+sensors.tcl has its own documentation at the beginning of file.
+Here is sensor's configuration
+label <Sensors>
+file <~/.sensors>
+Update command <sensors.tcl>
+Warning command  <mpg123 ~/mp3/cpu2hot.mp3>
+Alert command  <sudo shutdown -h now>
+
+
+INSTALL 
+tar zxvf fmonitor-xxx.tar.gz
+cd fmonitor-xxx
+# to install fmonitor.so 
+make
+make install
+# to install sensors.tcl 
+0. read documentation
+1. edit it!  
+2. put it somewhere in your bin path
+3. edit preferences of fmonitor in gkrellm configuration menu
+
+
+CREDITS
+Plugin is based on Bill Willson (bill@gkrellm.net) demo programs
+and on fileread plugin by Henry Palonen (h@yty.net)
+Modified for GKrellM 2 by Jindrich Makovicka (makovick@kmlinux.fjfi.cvut.cz).
+Some bugfixes by Geoff Kuenning (geoff@cs.hmc.edu)

+ 21 - 0
fm_led.xpm

@@ -0,0 +1,21 @@
+/* XPM */
+static char * fm_led_xpm[] = {
+"5 15 3 1",
+" 	c #FFFF00000000",
+".	c #FFFFFFFF0000",
+"X	c None s None",
+" . . ",
+".   .",
+"  .  ",
+".   .",
+" . . ",
+"X.X.X",
+".XXX.",
+"XXXXX",
+".XXX.",
+"X.X.X",
+"XXXXX",
+"XXXXX",
+"XXXXX",
+"XXXXX",
+"XXXXX"};

+ 838 - 0
fmonitor.c

@@ -0,0 +1,838 @@
+/* GKrellM (C) 1999-2000 Bill Wilson
+|
+|  Author:  Bill Wilson    bill@gkrellm.net
+|  Latest versions might be found at:  http://gkrellm.net
+|
+|  This program is free software which I release under the GNU General Public
+|  License. You may redistribute and/or modify this program under the terms
+|  of that 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.
+|
+|  To get a copy of the GNU General Puplic License,  write to the
+|  Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+/* File Monitor plugin for GKrellM
+|
+|  Copyright (C) 2000  Anatoly Asviyan
+|
+|  Author:  Anatoly Asviyan <aanatoly@linuxmail.org>
+|
+|  Version: for gkrellm version 0.10.4 and up
+|
+|  See README for details
+*/
+
+
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <gkrellm2/gkrellm.h>
+#include "fm_led.xpm"
+
+
+static char *plugin_info[] =
+{
+"File Monitor plugin for GKrellM\n"
+"Copyright (C) 2001 Anatoly Asviyan\n"
+"aanatoly@linuxmail.org\n"
+"Released under GPL\n"
+"Modified by Jindrich Makovicka\n",
+"makovick@kmlinux.fjfi.cvut.cz\n"
+"\n"
+"This plugin monitors a file and displays its contens in gkrellm. File can have\n"
+"multiple rows of the form 'name : value : [flag]'. If flag non-empty then\n"
+"for WARNING value - the orange led will light up and for ALERT value - red.\n"
+"For example\n"
+"  CPU:50.8:ALERT\n"
+"  SBr:33.4:WARNING\n"
+"  Fan1:4560:\n"
+"or\n"
+"  Temp:31 C:\n"
+"  Hum:49 %:\n"
+"\n"
+"Plugin can monitor multiple files and for each file, you can specify\n"
+"the following:\n",
+"<i>Label", " - label of gkrellm panel\n",
+"<i>File to monitor", " - file to monitor.\n",
+"If preceded with a pipe (\"|\"), it will be instead executed in the shell\n",
+"and the result will be read from standard output\n",
+"<i>Update program", " - the program to update a monitored file.\n", 
+"<i>Warning command", " - any shell command to run whenever warning flag is set.\n", 
+"<i>Alert command", " - any shell command to run whenever alert flag is set.\n",
+"<i>Check interval", " - how often to check.\n"
+"\n",
+"<b>CREDITS\n",
+"Plugin is based on Bill Willson (bill@gkrellm.net) demo programs\n"
+"and on fileread plugin by Henry Palonen (h@yty.net)\n"
+"\n"
+};
+
+
+#define FM_CONFIG_NAME     "FMonitor"   /* Name in the configuration window */
+#define FM_STYLE_NAME      "fmonitor"   /* Theme subdirectory name and gkrellmrc
+ */
+
+#define LEN             1024
+#define MAXFILENUM        10
+#define MAXPARAMNUM       10
+#define MAXARGNUM         20
+
+//#define HORIZONTALLY_CENTERED_LED
+
+enum { FM_LABEL, FM_FILE, FM_UPDATE, FM_WARN, FM_ALERT, FM_INTERVAL, FM_ENTRY_NUM };
+
+typedef struct {
+    gchar *text[FM_ENTRY_NUM];
+    gint pid; //pid of update command
+    int ticker;
+} fm_data;
+
+static GkrellmMonitor *monitor;
+
+typedef struct {
+    GkrellmPanel *panel;
+    GkrellmDecal *label;
+    GkrellmDecal *led[MAXPARAMNUM];
+    GkrellmDecal *name[MAXPARAMNUM];
+    GkrellmDecal *value[MAXPARAMNUM];
+    int fn[MAXPARAMNUM];
+    int rownum, y;
+} fm_gui;
+
+static fm_data fmc[MAXFILENUM];
+static fm_gui  fmg[MAXFILENUM];
+static int fmnum = 0, cnum = 0, selrow = -1;
+
+extern struct tm current_tm;
+
+//static Panel    *panel;
+//static Decal    *label_text;
+static gint      style_id;
+static gboolean	force_update;
+
+
+static GdkPixmap *ledp, *ledm;
+
+static GtkWidget *fm_vbox;
+static GtkWidget *entry[FM_ENTRY_NUM];
+static GtkWidget *config_list;
+static GtkWidget *btn_enter, *btn_del;
+
+static char *config_name[FM_ENTRY_NUM] = {
+    "label", "file", "update", "warn", "alert", "interval"
+};
+
+
+#define MY_FREE(x) if(x) {g_free(x);(x)=NULL;}
+
+
+static void destroy_fm_panels();
+static void create_fm_panels(int first_create);
+static void run_update_cmds();
+
+
+static gint
+panel_expose_event(GtkWidget *widget, GdkEventExpose *ev)
+{
+    int i;
+
+    for (i = 0; i < fmnum; i++) {
+		if (widget == fmg[i].panel->drawing_area)
+			gdk_draw_pixmap(widget->window,
+				widget->style->fg_gc[GTK_WIDGET_STATE (widget)],
+				fmg[i].panel->pixmap, ev->area.x, ev->area.y, ev->area.x, ev->area.y,
+				ev->area.width, ev->area.height);
+    }
+    return FALSE;
+}
+
+/* Shamelessly ripped from the Gtk docs. */
+static void fr_message(gchar *message)
+{
+    GtkWidget *dialog, *label, *okay_button;
+    dialog = gtk_dialog_new();
+    label = gtk_label_new (message);
+    okay_button = gtk_button_new_with_label("OK");
+    gtk_signal_connect_object (GTK_OBJECT (okay_button), "clicked",
+		GTK_SIGNAL_FUNC (gtk_widget_destroy), GTK_OBJECT(dialog));
+    gtk_container_add (GTK_CONTAINER (GTK_DIALOG(dialog)->action_area),
+		okay_button);
+    gtk_container_add (GTK_CONTAINER (GTK_DIALOG(dialog)->vbox),
+		label);
+    gtk_widget_show_all (dialog);
+}
+
+static void
+destroy_decal(GkrellmPanel *p, GkrellmDecal *d)
+{
+	p->decal_list = g_list_remove(p->decal_list, d);
+	gkrellm_destroy_decal(d);
+}
+
+static void
+update_plugin()
+{
+    GkrellmStyle *style;
+    GkrellmTextstyle *ts, *ts_alt;
+    int x, h, r, i, rownum, ph, drawing_leds, changed = 0;
+    int alert = 0, warning = 0;
+    char s[LEN];
+    FILE *fp;
+	
+    /* animation of alert and warning led */
+    for (i = 0; i < fmnum; i++) {
+	drawing_leds = FALSE;
+	for (r = 0; r < fmg[i].rownum; ++r) {
+	    if (fmg[i].fn[r] < 2) {
+		x = ((GK.timer_ticks % 10) < 2) ? 2 : fmg[i].fn[r];
+		gkrellm_draw_decal_pixmap(fmg[i].panel, fmg[i].led[r],x);
+		drawing_leds = TRUE;
+	    }
+	}
+	if (drawing_leds)
+	    gkrellm_draw_panel_layers(fmg[i].panel);
+    }		
+
+    if (!GK.second_tick)
+	return;
+
+    /* time to check monitored file(s) */
+    force_update = FALSE;
+    style = gkrellm_meter_style(style_id);
+    ts = gkrellm_meter_textstyle(style_id);
+    ts_alt = gkrellm_meter_alt_textstyle(style_id);
+
+    for (i = 0; i < fmnum; i++) {
+	int interval = atoi(fmc[i].text[FM_INTERVAL]);
+	if (interval < 1 || interval > 3600) interval = 60;
+
+	if (++fmc[i].ticker >= interval) {
+	    fmc[i].ticker = 0;
+	} else {
+	    continue;
+	}
+
+	ph = fmg[i].panel->h;
+	rownum = 0;
+	if (fmc[i].text[FM_FILE][0] == '|') {
+//		    fprintf(stderr, "popen %s\n", fmc[i].text[FM_FILE]+1);
+	    fp = popen(fmc[i].text[FM_FILE]+1, "r");
+	} else {
+	    fp = fopen(fmc[i].text[FM_FILE], "r");
+	}
+	if (fp) {
+	    /* read at most MAXPARAMNUM valid rows from file */
+	    while (fgets(s, LEN, fp) && (rownum < MAXPARAMNUM)) {
+		char *sname, *svalue, *sled;
+
+//				fprintf(stderr, "str %s\n", s);
+
+		sname = strtok(s, ":");
+		if (!sname || *sname == '\0')
+		    continue;
+		svalue = strtok(NULL, ":");
+		if (!svalue || *svalue == '\0')
+		    continue;
+		sled = strtok(NULL, " \n\t");
+		rownum++;
+		/* create decals for new row */
+		if (rownum > fmg[i].rownum) {
+		    changed = 1;
+		    r = rownum - 1;
+#ifdef HORIZONTALLY_CENTERED_LED
+		    /* Create led first so it will be behind name and value */
+		    /* name at left margin, value at right margin, led center */
+		    fmg[i].led[r] = gkrellm_create_decal_pixmap(fmg[i].panel,
+								ledp, ledm, 3, style, 0, fmg[i].y);		
+		    fmg[i].led[r]->x = (gkrellm_chart_width() - fmg[i].led[r]->w) / 2;
+		    fmg[i].name[r] = gkrellm_create_decal_text(fmg[i].panel,
+							       "VCOR2", ts_alt, style, -1, fmg[i].y, 0);
+		    fmg[i].value[r] = gkrellm_create_decal_text(fmg[i].panel,
+								"5555", ts_alt, style, 0, fmg[i].y, 0);
+		    fmg[i].value[r]->x = gkrellm_chart_width() - fmg[i].value[r]->w
+			- style->margin;
+#else
+		    fmg[i].led[r] = gkrellm_create_decal_pixmap(fmg[i].panel,
+								ledp, ledm, 3, style, -1, fmg[i].y);		
+		    x = style->margin.left + 5;
+		    fmg[i].name[r] = gkrellm_create_decal_text(fmg[i].panel,
+							       "VCOR2", ts_alt, style, x, fmg[i].y, 0);
+		    //					fmg[i].value[r] = gkrellm_create_decal_text(fmg[i].panel,
+		    //						"5555", ts_alt, style, x+fmg[i].name[r]->w, fmg[i].y, 0);
+		    /* Bill: Do you think value decal should be at right margin ? */
+		    /* Anatoly: Yes I do, I added it to TODO list */
+		    fmg[i].value[r] = gkrellm_create_decal_text(fmg[i].panel,
+								"5555", ts_alt, style, 0, fmg[i].y, 0);
+		    fmg[i].value[r]->x = gkrellm_chart_width()
+			- fmg[i].value[r]->w - style->margin.left;
+#endif
+		    h = fmg[i].name[r]->h;
+		    if (fmg[i].value[r]->h > h)
+			h = fmg[i].value[r]->h;
+		    if (fmg[i].led[r]->h < h)	/* vertically center led */
+			fmg[i].led[r]->y += (h - fmg[i].led[r]->h) / 2;
+		    fmg[i].y += h + 1;
+
+		    fmg[i].rownum++;
+
+		}
+				
+		if (!sled || !(*sled)) {
+		    fmg[i].fn[rownum-1] = 2;
+		} else if (!strcmp(sled, "WARNING")) {
+		    fmg[i].fn[rownum-1] = 1;
+		    warning = 1;
+		} else {
+		    fmg[i].fn[rownum-1] = 0;
+		    alert = 1;
+		}
+		gkrellm_draw_decal_pixmap(fmg[i].panel, fmg[i].led[rownum-1], fmg[i].fn[rownum-1]);
+//				fprintf(stderr, "update led pixmap\n");
+		gkrellm_draw_decal_text(fmg[i].panel, fmg[i].name[rownum-1], sname, -1);
+		gkrellm_draw_decal_text(fmg[i].panel, fmg[i].value[rownum-1], svalue, -1);
+
+	    }
+	    if (fmc[i].text[FM_FILE][0] == '|') {
+		pclose(fp);
+	    } else {
+		fclose(fp);
+	    }
+	}
+	/* destroys unused decals */
+	while (fmg[i].rownum > rownum) {
+	    changed = 1;
+	    h = fmg[i].name[fmg[i].rownum-1]->h;
+	    if (fmg[i].value[fmg[i].rownum-1]->h > h)
+		h = fmg[i].value[fmg[i].rownum-1]->h;
+	    fmg[i].y -= h + 1;
+	    destroy_decal(fmg[i].panel, fmg[i].led[fmg[i].rownum-1]);
+	    destroy_decal(fmg[i].panel, fmg[i].name[fmg[i].rownum-1]);
+	    destroy_decal(fmg[i].panel, fmg[i].value[fmg[i].rownum-1]);
+	    fmg[i].rownum--;
+	}
+
+	//gkrellm_monitor_height_adjust(fmg[i].panel->h - ph);
+	if (changed) {
+	    gkrellm_panel_configure(fmg[i].panel, NULL, style);
+	    gkrellm_panel_create(fm_vbox, monitor, fmg[i].panel);
+	    if (fmc[i].text[FM_LABEL]) {
+		gkrellm_draw_decal_text(fmg[i].panel, fmg[i].label,
+					fmc[i].text[FM_LABEL], -1);
+	    }
+	}
+	if (warning)
+	    system(fmc[i].text[FM_WARN]);
+	if (alert)
+	    system(fmc[i].text[FM_ALERT]);
+	gkrellm_draw_panel_layers(fmg[i].panel);
+    }
+
+    return;
+}
+
+/* creates "Sensors" monitor panel. the panel consists of label,
+ * and led/name/value pair for each row found in rc_file
+ */
+static void
+create_plugin(GtkWidget *vbox, gint first_create)
+{
+    fm_vbox = vbox;
+    create_fm_panels(first_create);
+	force_update = TRUE;
+}
+
+static void
+destroy_fm_panels()
+{
+    int i;
+
+    for (i = 0; i < fmnum; i++) {
+		if (fmg[i].panel) {
+//			gkrellm_monitor_height_adjust(-fmg[i].panel->h);
+//			gkrellm_destroy_decal_list(fmg[i].panel);
+//			gkrellm_destroy_krell_list(fmg[i].panel);
+			gkrellm_panel_destroy(fmg[i].panel);
+//			g_free(fmg[i].panel);
+			fmg[i].panel = NULL;
+			fmg[i].rownum = 0;
+		}
+    }
+}
+
+static void
+create_fm_panels(int first_create)
+{
+    GkrellmStyle *style;
+    GkrellmTextstyle *ts, *ts_alt;
+    int i;
+    GkrellmPiximage *led = NULL;
+
+    style = gkrellm_meter_style(style_id);
+    ts = gkrellm_meter_textstyle(style_id);
+    ts_alt = gkrellm_meter_alt_textstyle(style_id);
+
+	/* gkrellm_load_image will kill red_led and orange_led if not NULL */
+    gkrellm_load_piximage("fm_led", fm_led_xpm, &led, FM_STYLE_NAME);
+    gkrellm_scale_piximage_to_pixmap(led, &ledp, &ledm, 0, 0);
+
+    for (i = 0; i < fmnum; i++) {
+		//int y;
+
+		if (first_create)
+			fmg[i].panel = gkrellm_panel_new0();
+		else {
+			gkrellm_destroy_krell_list(fmg[i].panel);
+			gkrellm_destroy_decal_list(fmg[i].panel);
+			fmg[i].rownum = 0;
+		}
+		fmg[i].panel->textstyle = ts;        /* would be used for a panel label */
+
+		fmg[i].y = 0;
+		if (fmc[i].text[FM_LABEL]) {
+			fmg[i].label = gkrellm_create_decal_text(fmg[i].panel,
+				fmc[i].text[FM_LABEL], ts, style, -1, -1, -1);
+			fmg[i].y = fmg[i].label->y + fmg[i].label->h;
+		}
+		else  /* If no label, start remaining decals at top margin */
+			gkrellm_get_top_bottom_margins(style, &fmg[i].y, NULL);
+
+		gkrellm_panel_configure(fmg[i].panel, NULL, style);
+		//panel->label->h_panel += 2;         /* Some bottom margin */
+		gkrellm_panel_create(fm_vbox, monitor, fmg[i].panel);
+
+		if (fmc[i].text[FM_LABEL]) {
+			gkrellm_draw_decal_text(fmg[i].panel, fmg[i].label,
+				fmc[i].text[FM_LABEL], 1);
+		}
+		if (first_create)
+			gtk_signal_connect(GTK_OBJECT (fmg[i].panel->drawing_area), "expose_event",
+				(GtkSignalFunc) panel_expose_event, NULL);
+		gkrellm_draw_panel_layers(fmg[i].panel);
+    }
+	if (first_create)
+		run_update_cmds();
+}
+
+
+
+
+static void
+item_sel(GtkWidget *clist, gint row, gint column,
+    GdkEventButton *event,
+    gpointer data )
+{
+    gchar *tmp;
+    int i;
+
+    selrow = row;
+    for (i = 0; i < FM_ENTRY_NUM; i++) {
+		if (gtk_clist_get_text(GTK_CLIST(config_list), row, i, &tmp)) {
+			gtk_entry_set_text(GTK_ENTRY(entry[i]), tmp);
+		} else {
+			fprintf(stderr, "Strange: can't read %d-th col data "
+				"of %d selected row\n", i, row);
+		}
+    }
+    gtk_widget_set_sensitive(btn_del, TRUE);
+    return;
+}
+
+
+static void
+item_unsel(GtkWidget *clist, gint row, gint column, GdkEventButton *event,
+    gpointer data )
+{
+    int i;
+
+    selrow = -1;
+    for (i = 0; i < FM_ENTRY_NUM; i++) {
+		gtk_entry_set_text(GTK_ENTRY(entry[i]), "");
+    }
+
+    gtk_widget_set_sensitive(btn_del, FALSE);
+    return;
+}
+
+static void
+on_add_click(GtkButton *button, gpointer *data)
+{
+    char str[80];
+    int  i;
+    fm_data c;
+
+    if (!*gtk_entry_get_text(GTK_ENTRY(entry[FM_FILE]))) {
+		sprintf(str, "You must specify file to monitor.\n");
+		fr_message(str);
+		return;
+    }
+
+
+    if (selrow >= 0) {
+		/* we're editing existing entry */
+		gtk_clist_freeze( GTK_CLIST(config_list) );
+		for (i = 0; i < FM_ENTRY_NUM; i++) {
+			gtk_clist_set_text(GTK_CLIST(config_list), selrow, i,
+				gtk_entry_get_text(GTK_ENTRY(entry[i])));
+		}
+		gtk_clist_thaw( GTK_CLIST(config_list) );
+    } else {
+		/* we're adding new row */
+		if (cnum == MAXFILENUM) {
+			sprintf(str, "Maximum (%d) files has reached.\nSorry.\n",
+				MAXFILENUM);
+			fr_message(str);
+			return;
+		}
+		cnum++;
+
+		for (i = 0; i < FM_ENTRY_NUM; i++) {
+			c.text[i] = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry[i])));
+		}
+		gtk_clist_append (GTK_CLIST(config_list), c.text);
+		for (i = 0; i < FM_ENTRY_NUM; i++) {
+		    g_free(c.text[i]);
+		}
+    }
+
+    return;
+}
+
+
+static void
+on_del_click(GtkButton *button, gpointer *data)
+{
+    if (selrow == -1) return;
+
+    gtk_clist_remove(GTK_CLIST(config_list), selrow);
+    cnum--;
+}
+
+
+static void
+create_config_tab(GtkWidget *tab_vbox)
+{
+    GtkWidget		*vbox;
+    GtkWidget		*tabs;
+    GtkWidget               *text;
+    GtkWidget               *btn_hbox;
+    gchar                   *titles[]={"Lable","File",
+				       "Update Command",
+				       "Warning Command",
+				       "Alert Command",
+				       "Interval"};
+    GtkWidget *edit_table, *scrolled_window;
+    GtkWidget *llabel, *lfile, *lupdate, *lwarn, *lalert, *linterval, *linterval2;
+
+    gint                    i;
+    //item_t                  *item;
+
+    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);
+
+    /* Preferences Tab */
+    vbox = gkrellm_gtk_notebook_page(tabs, "Preferences");
+
+    // Edit table
+    edit_table = gtk_table_new(3, 5, FALSE);
+    llabel = gtk_label_new("Label:");
+    gtk_misc_set_alignment (GTK_MISC (llabel), 1, 1);
+    gtk_table_attach(GTK_TABLE(edit_table), llabel, 0, 1, 0, 1, GTK_FILL, 0, 1, 
+1);
+
+    entry[FM_LABEL] = gtk_entry_new_with_max_length(9);
+    gtk_table_attach(GTK_TABLE(edit_table), entry[FM_LABEL], 1, 2, 0, 1, 0, 0, 1
+, 1);
+    llabel = gtk_label_new(" ");
+    gtk_misc_set_alignment (GTK_MISC (llabel), 1, 1);
+    gtk_table_attach(GTK_TABLE(edit_table), llabel, 2, 3, 0, 1,
+		GTK_FILL|GTK_EXPAND, 0, 1, 1);
+
+
+    lfile = gtk_label_new("File to monitor:");
+    gtk_misc_set_alignment (GTK_MISC (lfile), 1, 1);
+    gtk_table_attach(GTK_TABLE(edit_table), lfile,  0, 1, 1, 2, GTK_FILL, 0, 1, 
+1);
+
+    entry[FM_FILE] = gtk_entry_new_with_max_length(255);
+    gtk_table_attach(GTK_TABLE(edit_table), entry[FM_FILE], 1, 3, 1, 2,
+		GTK_FILL|GTK_EXPAND, 0, 1, 1);
+
+    lupdate = gtk_label_new("Update Command:");
+    gtk_misc_set_alignment (GTK_MISC (lupdate), 1, 1);
+    gtk_table_attach(GTK_TABLE(edit_table), lupdate, 0, 1, 2, 3, GTK_FILL, 0, 1,
+ 1);
+
+    entry[FM_UPDATE] = gtk_entry_new_with_max_length(255);
+    gtk_table_attach(GTK_TABLE(edit_table), entry[FM_UPDATE], 1, 3, 2, 3,
+		GTK_FILL, 0, 1, 1);
+
+    lwarn = gtk_label_new("Warning Command:");
+    gtk_misc_set_alignment (GTK_MISC (lwarn), 1, 1);
+    gtk_table_attach(GTK_TABLE(edit_table), lwarn, 0, 1, 3, 4, GTK_FILL, 0, 1, 1
+);
+
+    entry[FM_WARN] = gtk_entry_new_with_max_length(255);
+    gtk_table_attach(GTK_TABLE(edit_table), entry[FM_WARN], 1, 3, 3, 4,
+		GTK_FILL, 0, 1, 1);
+
+    lalert = gtk_label_new("Alert Command:");
+    gtk_misc_set_alignment (GTK_MISC (lalert), 1, 1);
+    gtk_table_attach(GTK_TABLE(edit_table), lalert, 0, 1, 4, 5, GTK_FILL, 0, 1, 1);
+
+    entry[FM_ALERT] = gtk_entry_new_with_max_length(255);
+    gtk_table_attach(GTK_TABLE(edit_table), entry[FM_ALERT], 1, 3, 4, 5,
+		GTK_FILL, 0, 1, 1);
+
+    linterval = gtk_label_new("Check Interval:");
+    gtk_misc_set_alignment (GTK_MISC (linterval), 1, 1);
+    gtk_table_attach(GTK_TABLE(edit_table), linterval, 0, 1, 5, 6, GTK_FILL, 0, 1, 1);
+
+    entry[FM_INTERVAL] = gtk_entry_new_with_max_length(4);
+    gtk_table_attach(GTK_TABLE(edit_table), entry[FM_INTERVAL], 1, 2, 5, 6,
+		GTK_FILL, 0, 1, 1);
+
+    linterval2 = gtk_label_new("seconds");
+    gtk_misc_set_alignment (GTK_MISC (linterval2), 0, 1);
+    gtk_table_attach(GTK_TABLE(edit_table), linterval2, 2, 3, 5, 6, GTK_FILL, 0, 1, 1);
+
+    // Buttons to accept/reject config. data in the edit box
+    btn_hbox = gtk_hbox_new(FALSE, 5);
+    btn_enter=gtk_button_new_with_label("Enter");
+    gtk_signal_connect(GTK_OBJECT(btn_enter), "clicked",
+		GTK_SIGNAL_FUNC(on_add_click),NULL);
+    btn_del=gtk_button_new_with_label("Delete");
+    gtk_widget_set_sensitive(btn_del, FALSE);
+    gtk_signal_connect(GTK_OBJECT(btn_del), "clicked",
+		GTK_SIGNAL_FUNC(on_del_click),NULL);
+    gtk_box_pack_start(GTK_BOX(btn_hbox), btn_enter, TRUE, FALSE, 2);
+    gtk_box_pack_start(GTK_BOX(btn_hbox), btn_del, TRUE, FALSE, 2);
+
+    // List with saved configurations
+    scrolled_window = gtk_scrolled_window_new (NULL, NULL);
+    gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
+		GTK_POLICY_ALWAYS, GTK_POLICY_ALWAYS);
+
+    config_list = gtk_clist_new_with_titles(6,titles);
+    gtk_container_add(GTK_CONTAINER(scrolled_window), config_list);
+    gtk_signal_connect(GTK_OBJECT(config_list), "select-row",
+		GTK_SIGNAL_FUNC(item_sel), NULL);
+    gtk_signal_connect(GTK_OBJECT(config_list), "unselect-row",
+		GTK_SIGNAL_FUNC(item_unsel), NULL);
+    gtk_clist_set_selection_mode(GTK_CLIST(config_list), GTK_SELECTION_SINGLE);
+    for (i=0; i<6; i++) {
+		int width;
+		switch (i) {
+		case 0: width=70;break;
+		case 1: width=80;break;
+		default: width=150;break;
+		}
+		gtk_clist_set_column_width(GTK_CLIST(config_list), i, width);
+    }
+    for (i = 0; i < fmnum; i++)
+		gtk_clist_append(GTK_CLIST(config_list), fmc[i].text);
+    cnum = fmnum;
+    //gtk_clist_columns_autosize(GTK_CLIST(config_list));
+
+
+    gtk_box_pack_start(GTK_BOX(vbox), edit_table, FALSE, FALSE, 2);
+    gtk_box_pack_start(GTK_BOX(vbox), btn_hbox, FALSE, FALSE, 2);
+    gtk_box_pack_start(GTK_BOX(vbox), scrolled_window, TRUE, TRUE, 2);
+
+    /* Info Tab */
+    vbox = gkrellm_gtk_notebook_page(tabs, "Info");
+    text = gkrellm_gtk_scrolled_text_view(vbox, NULL, GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
+    gkrellm_gtk_text_view_append_strings(text,plugin_info,sizeof(plugin_info)/sizeof(gchar *));
+
+}
+
+
+static void
+save_config(FILE *f)
+{
+    int i, j;
+    for (i = 0; i < fmnum; i++) {
+		for (j = 0; j < FM_ENTRY_NUM; j++) {
+			fprintf(f, "FMonitor %s:%d:%s\n", config_name[j], i,
+				fmc[i].text[j] ? fmc[i].text[j] : "");
+		}
+    }
+
+}
+
+static void
+load_config( gchar* arg )
+{
+    char *tmp, *tmp2,  *name, *value;
+    int rowno, i;
+
+    tmp = g_strdup(arg);
+
+    name = strtok(tmp, ":");
+    if (!name) return;
+
+    tmp2 = strtok(NULL, ":");
+    if (!tmp2) return;
+    rowno = atoi(tmp2);
+    if (rowno < MAXFILENUM) {
+		value = strtok(NULL, "\n");
+
+		for (i = 0; i < FM_ENTRY_NUM; i++) {
+			if (!strcmp(name, config_name[i])) {
+				if (value)
+					fmc[rowno].text[i] =  g_strdup(value);
+				else
+					fmc[rowno].text[i] =  g_strdup("");
+			}
+		}
+
+		fmc[rowno].ticker = 10000;
+
+		if (rowno+1 > fmnum) fmnum = rowno+1;
+    }
+    g_free(tmp);
+}
+
+static void
+del_fmc_entries()
+{
+    int i, no;
+
+	for (no = 0; no < fmnum; no++) {
+		for (i = 0; i < FM_ENTRY_NUM; i++) {
+			MY_FREE(fmc[no].text[i]);
+		}
+	}
+}
+  
+
+static void
+run_update_cmds()
+{
+	int i, pid, ac;
+	char *arg[MAXARGNUM], *cmd;
+
+	//fprintf(stderr, "run_update_cmds\n");
+	
+	for (i = 0; i < fmnum; i++) {
+		cmd = g_strdup(fmc[i].text[FM_UPDATE]);
+		ac = 0;
+		arg[ac] = strtok(cmd, " \n\t");
+		while ((ac < MAXARGNUM-1) && arg[ac]) 
+			arg[++ac] = strtok(NULL, " \n\t");
+		if (arg[0] && *(arg[0])) {
+			pid = fork();
+			if (pid) {
+				//papa
+				fmc[i].pid = pid;
+				g_free(cmd);
+			} else {
+				//son
+				execvp(arg[0], arg);
+				fprintf(stderr, "Can't execvp <%s>\n", fmc[i].text[FM_UPDATE]);
+				_exit(1);
+			}
+		}
+	}
+}
+	 
+static void
+kill_update_cmds()
+{
+	int i;
+
+	//fprintf(stderr, "kill_update_cmds\n");
+	
+	for (i = 0; i < fmnum; i++) {
+		if (fmc[i].pid) {
+			//fprintf(stderr, "\tkilling %d\n", fmc[i].pid);
+			kill(fmc[i].pid, SIGKILL);
+		}
+	}
+}
+
+
+static void
+apply_config()
+{
+    char *tmp;
+    int i;
+
+    selrow = -1;
+
+	//fprintf(stderr, "apply_config\n");
+	
+    item_unsel(GTK_WIDGET(config_list), 0, 0, NULL, NULL);
+	del_fmc_entries();
+	kill_update_cmds();
+    destroy_fm_panels();
+
+    fmnum = 0;
+    while (gtk_clist_get_text(GTK_CLIST(config_list), fmnum, 0, &tmp))    {
+		for (i = 0; i < FM_ENTRY_NUM; i++) {
+			if (gtk_clist_get_text(GTK_CLIST(config_list), fmnum, i, &tmp))
+				fmc[fmnum].text[i] = g_strdup(tmp);
+		}
+		fmc[fmnum].ticker = 10000;
+		fmnum++;
+		if (fmnum == MAXFILENUM)
+			break;
+    }
+
+    create_fm_panels(1);
+	force_update = TRUE;
+}
+
+
+static void my_wait(int signo)
+{
+	waitpid(-1, NULL, WNOHANG | WUNTRACED);
+}
+
+		 
+
+/* The monitor structure tells GKrellM how to call the plugin routines.
+*/
+static GkrellmMonitor  plugin_mon =
+{
+    FM_CONFIG_NAME,     /* Name, for config tab.    */
+    0,                  /* Id,  0 if a plugin       */
+    create_plugin,      /* The create function      */
+    update_plugin,      /* The update function      */
+    create_config_tab,  /* The config tab create function   */
+    apply_config,       /* Apply the config function        */
+
+    save_config,        /* Save user config     */
+    load_config,        /* Load user config     */
+    FM_CONFIG_NAME,     /* config keyword       */
+
+    NULL,               /* Undefined 2  */
+    NULL,               /* Undefined 1  */
+    NULL,               /* private      */
+
+    MON_MAIL,           /* 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.
+  */
+GkrellmMonitor *
+gkrellm_init_plugin()
+{
+
+    style_id = gkrellm_add_meter_style(&plugin_mon, FM_STYLE_NAME);
+    atexit(kill_update_cmds);
+    signal(SIGCHLD, my_wait);
+    monitor = &plugin_mon;
+    return &plugin_mon;
+}
+