/* gkrelltop.c    - requires GKrellM 1.2.0 or better
 * 
 *   gcc -fPIC `gtk-config --cflags` `imlib-config --cflags-gdk` -c gkrelltop.c
 *   gcc -shared -Wl -o gkrelltop.so gkrelltop.o
 *   gkrellm -p gkrelltop.so
 *
 * 
 * gkrelltop is a monitor to see the three most cpu or memory 
 * intensive processes. It shows the percentage cpu (memory) 
 * and the pid of the most active one on mouse over as a tooltip. 
 * If there is a program that is hoging the CPU or memory 
 * you can identify it in moment.
 * Double-Middleclicking a program name will send 
 * a signal (kill -9) to that program. 
 * Clicking will switch between cpu mode and memory mode.
 *
 * to test it out try 
 *    gkrellm -p gkrelltop.so
 *
 * Uses sources from wmtop -- a WindowMaker process view dock app
 *
 * Written by Adi Zaimi adizaimi@users.sourceforge.net
 *
 * This software is licensed through the GNU General Public License.
 */


/*    
 *
 * this part of the code is borrowed from the gkrellm demos 
*/

#include "gkrelltop.h"
#include "support.h"
#include "krell_image.xpm"

#define PLUGIN_CONFIG_KEYWORD        "gkrelltop"
#define CONFIG_NAME                  "gkrelltop"
#define UPDATE_TAU                    5     /* seconds */    
#define MAX_UPDATES_PER_TAU           15    /* HZ */    
#define TOOLTIP_DELAY                 300   /* ms */
#define MSGLEN                        256   /* characters */

/* variables */
static Monitor   * monitor;
static GtkWidget * gtk_numproc            = NULL;
static GtkWidget * gtk_threshold          = NULL;
static GtkWidget * gtk_updates            = NULL;
static GtkWidget * gtk_exclusions         = NULL;
static GtkWidget * gtk_show_nice          = NULL;
static GtkWidget * gtk_show_percent       = NULL;

struct entries {
    Panel           *panel;
    Decal           *decal_text; 
    GkrellmKrell    *krell_meter;
} entry[MAXITEMS];

struct process *best[MAXITEMS];

/* variables */
static GtkTooltips *panel_tooltip = NULL;
static gint g_style_id;
static gint g_mouseIn = -1;
static gint mouseInDelay = 0;
static gint g_display_tooltip = FALSE;

static gint g_threshold = 0;     
static gint g_numproc = 2;      /* start with 2 procesess */
static gint g_local_updates = MAX_UPDATES_PER_TAU;     

static gint g_master_modulus  = MAX_UPDATES_PER_TAU;
static gint g_update_HZ = 0;     
static gint g_show_percent = 0;

#ifdef GKRELLM2
static GkrellmTicks *GKp;
#endif

/* global exclusion_expression variable */
extern regex_t *exclusion_expression;
extern gint exclusionchanged;
static gchar s_exclusion [MSGLEN]; //string to exclude rexexp from proc list
/* global show_nice_processes variable */
extern int show_nice_processes;
extern enum modes pluginMode;
static char *g_pluginModeText[] = {"CPU:     \n", "MEM:     \n", "IO:      \n"};
static gchar g_tooltip_text[MSGLEN]; //will hold the tooltip onmouseover

pid_t g_tokill_pid;


static int  (*find_top_three)(struct process **best);


#ifdef    GKRELLM_HAVE_CLIENT_MODE_PLUGINS
/* ---------------- gkrelltop client mode interface -------------- */

static struct process   cache_best[MAXITEMS];
static gint             cache_ntop;
static gboolean         gkrelltop_server_available;

static gint             gkrelltopd_version_major;
static gint             gkrelltopd_version_minor;
static gint             gkrelltopd_version_rev;

static void top_client_setup(gchar *line)
{
    fprintf(stderr,"line %s", line);
    if (!strcmp(line, "available"))
        gkrelltop_server_available = TRUE;
    else if (!strncmp(line, "version ", 8))
        sscanf(line, "%*s %d %d %d", &gkrelltopd_version_major,
             &gkrelltopd_version_minor, &gkrelltopd_version_rev);
}

/* show numproc panels and hide the rest of the panels */
static void check_numproc()
{
    gint i = 0;
    while(i < MAXITEMS) {
        if(i < g_numproc)
            gkrellm_panel_show( entry[i].panel );
        else
            gkrellm_panel_hide( entry[i].panel );
        i++;
    }
}

static void top_client_data_from_server(gchar *line)
{
    gfloat  amount;
    gchar   which[32], item[128], name[128];
    gint    pid, n;

    if (sscanf(line, "%31s %127[^\n]", which, item) != 2)
       return;

    if (!strcmp(which, "ntop"))
    {
        cache_ntop = atoi(item);
        if (cache_ntop < 0 || cache_ntop > MAXITEMS)
            cache_ntop = 0;
            for (n = cache_ntop; n < MAXITEMS; ++n)
            {
                gkrellm_dup_string(&cache_best[n].name, "");
                cache_best[n].amount = 0;
                cache_best[n].pid = 0;
            }
    }
    else if( !strcmp(which, "best")
          && sscanf(item, "%d %127s %d %f", &n, name, &pid, &amount) == 4
          && n >= 0 && n < MAXITEMS)
    {
        gkrellm_dup_string(&cache_best[n].name, name);
        cache_best[n].pid = pid;
        cache_best[n].amount = amount;
    }
}

static gint gkrelltop_client_process_find_top_three(struct process **best)
{
    gint i;

    for (i = 0; i < g_numproc; ++i)
        best[i] = &cache_best[i];
    return cache_ntop;
}

/* ----------------- end of client mode interface ---------------- */
#endif


/* gets called everytime the panel gets refreshed if it was covered */
static gint top_expose_event(GtkWidget * widget, 
               GdkEventExpose * ev, Panel *panel)
{
    gdk_draw_pixmap(widget->window,
        widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
        panel->pixmap, ev->area.x, ev->area.y, ev->area.x,
        ev->area.y, ev->area.width, ev->area.height);
    return FALSE;
}

/* upon signal enter_notify_event */
static void top_enter_notify_event(GtkWidget * widget, 
            GdkEventCrossing * event, gpointer user_data)
{ 
    g_mouseIn = GPOINTER_TO_INT(user_data); /* mark where mouse moved into */
    mouseInDelay = 2; /* delay by two cycles repainting -- configurable? */
    g_display_tooltip = TRUE; /* redisplay */

    /* immediately display the tooltip instead of waiting for update */
    gtk_tooltips_set_tip(panel_tooltip, widget, g_tooltip_text, ""); 
}

/* upon signal motion_notify_event */
static void top_motion_notify_event(GtkWidget * widget, 
                GdkEventCrossing * event, gpointer user_data)
{
    g_display_tooltip = TRUE; /* redisplay */
}

/* upon signal leave_notify_event */
static void top_leave_notify_event(GtkWidget * widget, 
            GdkEventCrossing * event, gpointer user_data)
{
    g_mouseIn = -1;        /* change status; mouse is outside */
}

static void top_ok_dialog(GtkWidget *widget, GtkWidget *dialog)
{
    /* at this point send the signal to g_tokill_pid even though 
     * it might be a better idea to copy it in the framebuffer and the 
     * user kills it from a terminal.
     */
    kill(g_tokill_pid, 9);

    gtk_widget_destroy(dialog);
}

static void top_close_dialog(GtkWidget *widget, GtkWidget *dialog)
{
    gtk_widget_destroy(dialog);
}

static void top_message_dialog(const char * name, pid_t pid)
{

    GtkWidget   *dialog;
    GtkWidget   *vbox;
    GtkWidget   *hbox, *button;
    GtkWidget   *label;
    gchar        message[MSGLEN];


    g_tokill_pid = pid;

    snprintf(message, sizeof(message), "Kill process %s with pid %d?",name,pid);

    dialog = gtk_window_new(GTK_WINDOW_TOPLEVEL);
    gtk_window_set_title(GTK_WINDOW(dialog), "Kill process");
    gtk_window_set_wmclass(GTK_WINDOW(dialog), "Gkrellm_dialog", "Gkrellm");

    vbox = gtk_vbox_new(FALSE, 0);
    gtk_container_set_border_width(GTK_CONTAINER(vbox), 8);
    gtk_container_add(GTK_CONTAINER(dialog), vbox);

    label = gtk_label_new(message);
    gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_LEFT);

    /* pack the label with the message */
    gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);

    hbox = gtk_hbutton_box_new();
    gtk_button_box_set_layout(GTK_BUTTON_BOX(hbox), GTK_BUTTONBOX_END);
    gtk_box_pack_start(GTK_BOX(vbox), hbox, TRUE, FALSE, 2);

    button = gtk_button_new_from_stock(GTK_STOCK_OK);
    g_signal_connect(G_OBJECT(button), "clicked",
            G_CALLBACK(top_ok_dialog), dialog);
    gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0);

    button = gtk_button_new_from_stock(GTK_STOCK_CANCEL);
    g_signal_connect(G_OBJECT(button), "clicked",
            G_CALLBACK(top_close_dialog), dialog);
    gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0);

    gtk_widget_show_all(dialog);
}

/* upon signal button_press_event */
static void top_click_event(GtkWidget * widget, GdkEventButton * ev,
        gpointer user_data)
{
    if (ev->button == 3) {
        /* show the setup window on rightclick */
        gkrellm_open_config_window(monitor);
    }
    else if (ev->button==2 && (ev->state & GDK_CONTROL_MASK || 
                               ev->type == GDK_2BUTTON_PRESS)) {
        /* user_data has the number of the panel clicked */
        gint i=GPOINTER_TO_INT(user_data);
        if(best[i]) 
            top_message_dialog(best[i]->name, best[i]->pid);

    }
    else { /* left click: rotate between cpu/mem/io views */
#if defined(LINUX)
        pluginMode++;
        if(pluginMode > io)
            pluginMode = cpu;
#endif
    }
}

/* do the actual update of the plugin, separate if needed to be called
 * by some other function besides update_plugin() */
static void top_update_panels()
{
    if(mouseInDelay-- > 0)
        return;

    gchar display_text[64];
    bzero(display_text,sizeof(display_text));

    gint n = 0;
    gint i = 0;
    g_tooltip_text[0] = '\0';
    for (i = 0; i < MAXITEMS; i++) {
        best[i]=0;
    }

#ifdef  GKRELLM_HAVE_CLIENT_MODE_PLUGINS
    /* If user is running a gkrelltop version that can get server data but
    |  isn't, make sure user can know displayed data is from local host.
    */
    if (!gkrelltop_server_available && gkrellm_client_mode())
        snprintf(g_tooltip_text, sizeof(g_tooltip_text), "Localhost: %s\n\n",
            gkrellm_get_hostname());
#endif

    /* every tick update the process lists, and get data from either
    * a gkrellmd server or locally via top_three.c
    */
    n = (*find_top_three)(best);    /* Find the top three processes */

    /* cant have more than MAXITEMS, some error occured */
    if (n > MAXITEMS) {
        return;
    }

    if(g_display_tooltip)
        strncat(g_tooltip_text,g_pluginModeText[pluginMode],
                 sizeof(g_tooltip_text)-strlen(g_tooltip_text));

    for (i = 0; i < n; i++) 
    {
        if(best[i]->amount < g_threshold ) 
            break; //the next for will fill in with blanks

        if(g_show_percent)
            snprintf(display_text, sizeof(display_text),
                "%.0f%c %s", best[i]->amount,'%',best[i]->name);
        else 
            snprintf(display_text, sizeof(display_text)," %s", best[i]->name);


        if(g_display_tooltip) /* append at the end of g_tooltip_text */
        { 
            gchar tmp[MSGLEN];
            gchar ch = (i < n-1) ? '\n' : ' ' ; /* prnt \n only for 1 and 2*/

            if(pluginMode == cpu)
                snprintf(tmp,sizeof(tmp), "%d: %4.1f%c  %6.2d  %.30s%c", 
                    i+1, best[i]->amount, '%', best[i]->pid, best[i]->name, ch);
            else if(pluginMode == mem)
                snprintf(tmp,sizeof(tmp), "%d: %4.1f%c %6.2dMB %6.2d  %.30s%c", 
                    i+1, best[i]->amount, '%', best[i]->rss/MEG,
                    best[i]->pid, best[i]->name, ch);
            else if(pluginMode == io)
                snprintf(tmp,sizeof(tmp), 
                    "%d: %4.1f%c R:%6.0fKB W:%6.0fKB %6.2d  %.30s%c", 
                    i+1, best[i]->amount, '%', 
                    (float)(best[i]->io_read - best[i]->previous_io_read)/KIL,
                    (float)(best[i]->io_write - best[i]->previous_io_write)/KIL,
                    best[i]->pid, best[i]->name, ch);

            strncat(g_tooltip_text,tmp,sizeof(g_tooltip_text)-strlen(g_tooltip_text));
        }

        /* set the panels with the top three processes */
        gkrellm_draw_decal_text(entry[i].panel, 
                entry[i].decal_text,display_text, 0);

        /* best[i]->amount determines the ofset in the pixmap */
        gkrellm_update_krell(entry[i].panel, 
                entry[i].krell_meter, best[i]->amount ); 
    } // end for
    for(; i< g_numproc; i++) { //take care of the rest of the entries, 
                          //i set in the previous for loop

        /* set the panels with the top three processes, or with blank */
        gkrellm_draw_decal_text(entry[i].panel, entry[i].decal_text,"", 0);
        gkrellm_update_krell(entry[i].panel, entry[i].krell_meter, 0);
    } //end for

    /* update the tooltip with the recent proceses in the list */
    if (g_mouseIn >= 0 && g_display_tooltip) { 
        /* display  tooltip */
       gtk_tooltips_set_tip(panel_tooltip, entry[g_mouseIn].panel->drawing_area,
                                               g_tooltip_text, g_tooltip_text); 
    }

    //draw the newly updated pannel
    for (i=0; i < g_numproc; i++) {
        gkrellm_draw_panel_layers(entry[i].panel);
    }
}

/* recompute modulus value for the updated parameters */
static void recompute_modulus()
{
    static gint old_local_updates=0;

    gint itmp=gkrellm_update_HZ();
    /* only recompute the modulus if the value for g_update_HZ has changed
     * or the value for the local_updates has changed
     */
    if(g_update_HZ != itmp || old_local_updates != g_local_updates) {
        old_local_updates = g_local_updates;

        if(itmp > 0 && itmp < MAX_UPDATES_PER_TAU) 
            g_update_HZ = itmp;
        else 
            g_update_HZ = MAX_UPDATES_PER_TAU;

        g_master_modulus=(gint)(g_update_HZ*UPDATE_TAU/g_local_updates);
        if(g_master_modulus < 1) g_master_modulus = 1;
    }
}

/* the update_plugin function is called every timer_tick 
 * which is determined by the g_update_HZ() function
 */ 
static void top_update_plugin()
{


#ifdef GKRELLM2
    if (GKp->five_second_tick) {
        /* set the global update_HZ variable every five second ticks 
         * so if user changed the g_update_HZ, we will have 
         * the latest value 
         * */
        recompute_modulus();
    }

/* timer_ticks is a counter which is updated gkrellm_update_HZ() times a second
 * gkrellm_update_HZ() is determined by the 'Krell and LED updates per second'
 * from the general options tab of gkrellmwith a range between 2-20 hertz
 */

    if ( (GKp->timer_ticks % g_master_modulus) == 0 ) { 
        top_update_panels();  /* update the panels */
    }   

#else
    if (GK.five_second_tick) {
        /* set the global update_HZ variable every five second ticks 
         * so if user changed the g_update_HZ, we will have 
         * the latest  value 
         * */
        g_update_HZ = gkrellm_update_HZ();
        if(g_update_HZ <= 0 || g_update_HZ > 10) 
            g_update_HZ=5;
    }

    if ((GK.timer_ticks % (gint)(g_update_HZ*UPDATE_TAU)) == g_local_updates)
    {
        top_update_panels();  /* update the panels */
    }   
#endif

}


static void top_create_plugin(GtkWidget * vbox, gint first_create)
{
    Style *style;
    TextStyle *ts;
    gint y;
    gint i;

    /* assign the timer pointer to GKp global variable */
    GKp = gkrellm_ticks();

    /* See comments about first create in demo2.c
     */
    if (first_create) {
        for (i=0; i<MAXITEMS; i++) {
            entry[i].panel = gkrellm_panel_new0();   /* create new panel */
        }
    }

    style = gkrellm_meter_style(g_style_id);  /* set style of the meter */

    ts = gkrellm_meter_textstyle(g_style_id); //smaller font e brighter col 
    ts->font=gkrellm_default_font(0); //set to small_font

    GkrellmPiximage *image = NULL;
    gkrellm_load_piximage("krell_image", krell_image_xpm, &image, STYLE_NAME);
    gkrellm_set_style_krell_values_default(style,
                0,      // -3 justifies to bottom margin. 
                gdk_pixbuf_get_height(image->pixbuf)/3, //img_hight/strip_hight
                59,     // shift in x (xhot)
                KRELL_EXPAND_LEFT, 1,  //expansion type and movement steping
                0, 0);   //left and right margins, should be close to 0

    for (i=0; i<MAXITEMS; i++) 
    {
        /* create the krell--the image with the color */
        entry[i].krell_meter = gkrellm_create_krell(entry[i].panel,image,style);
        gkrellm_set_krell_full_scale(entry[i].krell_meter, 100, 1);
        gkrellm_monotonic_krell_values(entry[i].krell_meter, FALSE);
        //update the panel with the new meters
        gkrellm_update_krell(entry[i].panel, entry[i].krell_meter, 0);

        /* Create three text decal that will be used to display text.  Make it
         * the full panel width (minus the margins).  Position it at top border
         * y=2 and left margin x=-1 of the style.
         */
        entry[i].decal_text = gkrellm_create_decal_text(entry[i].panel, 
                "Ayl0", ts, style, -1, 2, -1);

        y=entry[i].decal_text->y + entry[i].decal_text->h - 3;

        /* offset the krell image under the text decal */
        gkrellm_move_krell_yoff(entry[i].panel, entry[i].krell_meter, y);
        
        /* needed if the krell goes on top of the text */
        gkrellm_decal_on_top_layer(entry[i].decal_text, TRUE);
    }

    for (i=0; i<MAXITEMS; i++) {
        /* Configure the panel to hold the above created decals, and create it*/
        gkrellm_panel_configure(entry[i].panel, "", style);
        gkrellm_panel_create(vbox, monitor, entry[i].panel);
    }


    /* connect the signals */
    
    if (first_create) 
    {
      for (i=0; i<MAXITEMS; i++) {
        gtk_signal_connect(GTK_OBJECT(entry[i].panel->drawing_area), 
          "expose_event", (GtkSignalFunc) top_expose_event, entry[i].panel);

        gtk_signal_connect(GTK_OBJECT(entry[i].panel->drawing_area),
          "enter_notify_event", (GtkSignalFunc) top_enter_notify_event, 
                GINT_TO_POINTER(i));

        gtk_signal_connect(GTK_OBJECT(entry[i].panel->drawing_area),
          "leave_notify_event", (GtkSignalFunc) top_leave_notify_event, NULL);

        gtk_signal_connect(GTK_OBJECT(entry[i].panel->drawing_area),
          "motion_notify_event" ,(GtkSignalFunc) top_motion_notify_event, NULL);

        gtk_signal_connect(GTK_OBJECT(entry[i].panel->drawing_area),
            "button_press_event", (GtkSignalFunc) top_click_event, 
                GINT_TO_POINTER(i));
      }

      panel_tooltip = gtk_tooltips_new();
    }


    for (i=0; i<MAXITEMS; i++) 
    {
        /* start with empty string */
        gtk_tooltips_set_tip(panel_tooltip, 
            entry[i].panel->drawing_area," \n \n ", "");

        gtk_tooltips_set_delay(panel_tooltip, TOOLTIP_DELAY);
        gtk_tooltips_enable(panel_tooltip);

        // draw the panel
        gkrellm_draw_panel_layers(entry[i].panel);
    }

    /* set the local_updates after reading from the config */
    if(g_local_updates > MAX_UPDATES_PER_TAU) 
        g_local_updates = MAX_UPDATES_PER_TAU;
    else if(g_local_updates <= 0) 
        g_local_updates = 1; 

    recompute_modulus();
    check_numproc();

    return;
}

static void top_create_plugin_tab(GtkWidget *tab_vbox) 
{
    GtkWidget *tabs;
    //GtkWidget *table;
    GtkWidget  *vbox, *vbox1, *hbox;
    //GtkWidget *button;
    GtkWidget *label;
    GtkWidget *text;
    //GSList *group;
    gchar *about_text;
    //gint i;

    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);

    /* -- setup -- */
    vbox = gkrellm_create_framed_tab(tabs, _("Setup"));
    vbox1 = gkrellm_framed_vbox(vbox, _("Visualisation Options"), 4,FALSE,0,2);

    /* How many processes to show */
    hbox = gtk_hbox_new(FALSE, 0);
    gtk_box_pack_start(GTK_BOX(vbox1), hbox, FALSE, FALSE, 0);
    label = gtk_label_new(_("Number of top processes to show (max 3):"));
    gtk_box_pack_start(GTK_BOX(hbox), label, TRUE, TRUE, 0);
    gkrellm_spin_button(hbox, &gtk_numproc, (gfloat) g_numproc, 1.0, 
             MAXITEMS, 1.0, 5.0, 0, 60, NULL, NULL, FALSE, _(""));


    hbox = gtk_hbox_new(FALSE, 0);
    gtk_box_pack_start(GTK_BOX(vbox1), hbox, FALSE, FALSE, 0);
    label = gtk_label_new(_("Show only processes above (in %):"));
    gtk_box_pack_start(GTK_BOX(hbox), label, TRUE, TRUE, 0);
    gkrellm_spin_button(hbox, &gtk_threshold, (gfloat) g_threshold, 
           0.0, 100.0, 1.0, 5.0, 0, 60, NULL, NULL, FALSE, _(""));

    hbox = gtk_hbox_new(FALSE, 0);
    gtk_box_pack_start(GTK_BOX(vbox1), hbox, FALSE, FALSE, 0);
    label = gtk_label_new(_("Frequency of updates (in 5 seconds):"));
    gtk_box_pack_start(GTK_BOX(hbox), label, TRUE, TRUE, 0);
    gkrellm_spin_button(hbox, &gtk_updates, (gfloat) g_local_updates, 1.0, 
            MAX_UPDATES_PER_TAU, 1.0, 5.0, 0, 60, NULL, NULL, FALSE, _(""));


    /* exclusions stuff */
    hbox = gtk_hbox_new(FALSE, 0);
    label = gtk_label_new(_("Exclusions (as regular expression ex: ^idle) : "));
    gtk_box_pack_start(GTK_BOX(hbox),label,TRUE,TRUE,0);
    gtk_exclusions = gtk_entry_new();

    if (strlen(s_exclusion) > 0)
        gtk_entry_set_text((GtkEntry*)gtk_exclusions, s_exclusion);

    gtk_box_pack_start(GTK_BOX(hbox),gtk_exclusions,TRUE,TRUE,8);
    gtk_box_pack_start(GTK_BOX(vbox1), hbox, FALSE, FALSE, 0);

    gtk_box_pack_start(GTK_BOX(vbox1), gtk_hseparator_new(), FALSE, FALSE, 8);

    /* nice processes */
    hbox = gtk_hbox_new(FALSE, 0);
    gtk_box_pack_start(GTK_BOX(vbox1), hbox, FALSE, FALSE, 0);
    gkrellm_check_button(hbox, &gtk_show_nice, show_nice_processes, TRUE, 0,
      _("Show nice processes"));

    /* percentage in front of process name */
    hbox = gtk_hbox_new(FALSE, 0);
    gtk_box_pack_start(GTK_BOX(vbox1), hbox, FALSE, FALSE, 0);
    gkrellm_check_button(hbox, &gtk_show_percent, g_show_percent, TRUE, 0,
      _("Show percentage in front of process names"));

    /* could have process activity as chart but many processes are ephemeral
     * also doubleclicking on a panel could kill (send a signal) to the process
     * and sorting by memory which is available in the top-three */

    /* -- About -- */
    about_text = g_strdup_printf(
      "gkrelltop version 2.2.13\n"
      "gkrellm top plugin\n"
      "Copyright (c) 2007-9 Adi Zaimi\n"
      "adizaimi@users.sourceforge.net\n"
      "http://gkrelltop.sourceforge.net\n\n"
      "Released under the GNU Public License");

    text = gtk_label_new(about_text);
    label = gtk_label_new("About");
    gtk_notebook_append_page(GTK_NOTEBOOK(tabs),text,label);
    g_free(about_text);

    return;
}

static void top_apply_exclusion() 
{
    if( strlen(s_exclusion) > 0 ) {
        static regex_t reg;
        exclusion_expression = &reg;
        regcomp(exclusion_expression, s_exclusion, REG_EXTENDED);
        exclusionchanged=1;
    }
    else if(exclusion_expression){
        exclusion_expression=0;
        exclusionchanged=1;
    }
}

static void top_apply_config() 
{
    GtkSpinButton *spin;
    
    spin = GTK_SPIN_BUTTON(gtk_numproc);
    //set the numproc
    g_numproc=gtk_spin_button_get_value_as_int(spin);
    check_numproc();

    spin = GTK_SPIN_BUTTON(gtk_threshold);
    //set the threshold
    g_threshold = gtk_spin_button_get_value_as_int(spin);
    //set the show_nice_processes variable from the checkbox
    show_nice_processes = GTK_TOGGLE_BUTTON(gtk_show_nice)->active;
    g_show_percent = GTK_TOGGLE_BUTTON(gtk_show_percent)->active;
    spin = GTK_SPIN_BUTTON(gtk_updates);
    //set how often to update the top list
    g_local_updates = gtk_spin_button_get_value_as_int(spin);

    //if the exclusions is set, build the exclusion_expression variable
    if (gtk_exclusions ) 
    {
        
        g_strlcpy(s_exclusion, gtk_entry_get_text((GtkEntry *) gtk_exclusions),
                                                         sizeof(s_exclusion) );
        top_apply_exclusion();
    }
    else if (exclusion_expression)
    {
        exclusion_expression=0;
    }

    /* safety check */
    if(g_local_updates > MAX_UPDATES_PER_TAU) 
        g_local_updates = MAX_UPDATES_PER_TAU;
    else if(g_local_updates <= 0) 
        g_local_updates = 1; 

    recompute_modulus();

    return;
}

static void top_save_config(FILE *f) 
{
   /* Save any configuration data we have in config lines in the format:
    PLUGIN_CONFIG_KEYWORD  config_keyword  data
    */

    fprintf(f, "%s numproc %d\n", PLUGIN_CONFIG_KEYWORD, g_numproc);
    fprintf(f, "%s threshold %d\n", PLUGIN_CONFIG_KEYWORD, g_threshold);
    fprintf(f, "%s show_nice_processes %d\n", 
            PLUGIN_CONFIG_KEYWORD, show_nice_processes);

    fprintf(f, "%s show_percent %d\n", PLUGIN_CONFIG_KEYWORD, g_show_percent);
    fprintf(f, "%s local_updates %d\n", PLUGIN_CONFIG_KEYWORD, g_local_updates);
    fprintf(f, "%s exclusion_expression %s\n", 
            PLUGIN_CONFIG_KEYWORD, s_exclusion);

    return;
}

static void top_load_config(gchar *config_line) 
{
    gchar   config_keyword[32], config_data[CFG_BUFSIZE];
    gint    n;

    if ((n = sscanf(config_line,"%31s %[^\n]",config_keyword,config_data)) != 2)
    {
        return;
    }

    if ( strcmp(config_keyword, "numproc")==0 ) {
        sscanf(config_data, "%d", &g_numproc);
    }
    else if ( strcmp(config_keyword, "threshold")==0 ) {
        sscanf(config_data, "%d", &g_threshold);
    }
    else if (strcmp(config_keyword, "show_nice_processes")==0) {
        sscanf(config_data, "%d", &show_nice_processes);
    }
    else if (strcmp(config_keyword, "show_percent")==0) {
        sscanf(config_data, "%d", &g_show_percent);
    }
    else if (strcmp(config_keyword, "local_updates")==0) {
        sscanf(config_data, "%d", &g_local_updates);
    }
    else if (strcmp(config_keyword, "exclusion_expression")==0) {
        sscanf(config_data, "%s", s_exclusion);
        top_apply_exclusion();
    }

    return;
}


/* The monitor structure tells GKrellM how to call the plugin routines.  */
static Monitor top_plugin_mon = {
    CONFIG_NAME,              /* Name, for config tab.                      */
    0,                        /* Id,  0 if a plugin                         */
    top_create_plugin,        /* The create function                        */
    top_update_plugin,        /* The update function                        */
    top_create_plugin_tab,    /* The config tab create function             */
    top_apply_config,         /* Apply the config function                  */
    top_save_config,          /* Save user config                           */
    top_load_config,          /* Load user config                           */
    PLUGIN_CONFIG_KEYWORD,    /* config keyword                             */

    NULL,                     /* Undefined 2                                */
    NULL,                     /* Undefined 1                                */
    NULL,                     /* private                                    */
  
    MON_CPU | MON_INSERT_AFTER, /* Insert plugin after this monitor          */
    NULL,                     /* Handle if a plugin, filled in by GKrellM   */
    NULL                      /* path if a plugin, filled in by GKrellM     */
};


/* initialise plugin -- required */
Monitor *init_plugin()
{
    g_style_id = gkrellm_add_meter_style(&top_plugin_mon, STYLE_NAME);

#ifdef    GKRELLM_HAVE_CLIENT_MODE_PLUGINS
    gkrellm_client_plugin_get_setup(GKRELLTOP_SERVE_NAME, top_client_setup);

    fprintf(stderr,"available %d", gkrelltop_server_available);
    if (gkrelltop_server_available)
    {
        find_top_three = gkrelltop_client_process_find_top_three;
        gkrellm_client_plugin_serve_data_connect(&top_plugin_mon,
                GKRELLTOP_SERVE_NAME, top_client_data_from_server);
    }
    else
        find_top_three = gkrelltop_process_find_top_three;
#else
        find_top_three = gkrelltop_process_find_top_three;
#endif

    monitor = &top_plugin_mon;
    return &top_plugin_mon;
}
