/* GKrellM
|  Copyright (C) 1999-2021 Bill Wilson
|
|  Author:  Bill Wilson
|  Latest versions might be found at:  https://gkrellm.srcbox.net
|
|
|  GKrellM 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 3 of the License, or
|  (at your option) any later version.
|
|  GKrellM 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, see http://www.gnu.org/licenses/
|
|
|  Additional permission under GNU GPL version 3 section 7
|
|  If you modify this program, or any covered work, by linking or
|  combining it with the OpenSSL project's OpenSSL library (or a
|  modified version of that library), containing parts covered by
|  the terms of the OpenSSL or SSLeay licenses, you are granted
|  additional permission to convey the resulting work.
|  Corresponding Source for a non-source form of such a combination
|  shall include the source code for the parts of OpenSSL used as well
|  as that of the covered work.
*/

#include "gkrellm.h"
#include "gkrellm-private.h"
#include "gkrellm-sysdeps.h"

typedef struct
	{
	gchar			*panel_label;
	GtkWidget		*vbox;
	GkrellmChart	*chart;
	GkrellmChartdata *forks_cd;
	GkrellmChartconfig *chart_config;
	gint			enabled;
	gboolean		extra_info;
	gint			save_label_position;

	gpointer		sensor_temp,
					sensor_fan;
	GkrellmDecal	*sensor_decal,
					*fan_decal;

	gint			n_users;
	gint			n_processes;
	gint			n_running;
	gulong			n_forks;
	gfloat			fload;
	}
	ProcMon;

ProcMon	proc;

static GkrellmProcReadProcFunc read_proc_data;
static GkrellmProcReadUsersFunc read_user_data;

GkrellmAlert	*load_alert,
				*processes_alert,
				*users_alert;

static gboolean
setup_proc_interface(void)
	{
	if (!read_proc_data && !_GK.client_mode && gkrellm_sys_proc_init())
		{
		read_proc_data = gkrellm_sys_proc_read_data;
		read_user_data = gkrellm_sys_proc_read_users;
		}
	return read_proc_data ? TRUE : FALSE;
	}

void
gkrellm_proc_client_divert(GkrellmProcReadProcFunc read_proc_func,
			GkrellmProcReadUsersFunc read_users_func)
	{
	read_proc_data = read_proc_func;
	read_user_data = read_users_func;
	}

void
gkrellm_proc_assign_data(gint n_processes, gint n_running,
		gulong n_forks, gfloat load)
	{
	proc.n_processes = n_processes;
	proc.n_running = n_running;
	proc.n_forks = n_forks;
	proc.fload = load;
	}

void
gkrellm_proc_assign_users(gint n_users)
	{
	proc.n_users = n_users;
	}

/* ======================================================================== */

  /* GkrellmCharts are integer only and load is a small real, so scale all load
  |  reading by 100.
  */
#define	LOAD_SCALING	100.0

static GkrellmMonitor	*mon_proc;

static GkrellmLauncher	proc_launch;

static gint		style_id;
static gboolean	show_temperature,
				show_fan;
static gboolean	sensor_separate_mode;
static gint		fork_scaling = 1;
static gchar	*text_format,
				*text_format_locale;

static gboolean	new_text_format = TRUE;

static void
format_proc_data(ProcMon *p, gchar *src_string, gchar *buf, gint size)
	{
	GkrellmChart	*cp;
	gchar			c, *s;
	gint			len, value;

	if (!buf || size < 1)
		return;
	--size;
	*buf = '\0';
	if (!src_string)
		return;
	cp = p->chart;
	for (s = src_string; *s != '\0' && size > 0; ++s)
		{
		len = 1;
		if (*s == '$' && *(s + 1) != '\0')
			{
			value = -1;
			if ((c = *(s + 1)) == 'L')
				len = snprintf(buf, size, "%.1f",
					(gfloat) gkrellm_get_chart_scalemax(cp) / LOAD_SCALING);
			else if (c == 'F' && fork_scaling > 0)
				len = snprintf(buf, size, "%d",
						gkrellm_get_chart_scalemax(cp) / fork_scaling);
			else if (c == 'H')
				len = snprintf(buf, size, "%s", gkrellm_sys_get_host_name());
			else if (c == 'l')
				len = snprintf(buf, size, "%.1f", p->fload);
			else if (c == 'p')
				value = p->n_processes;
			else if (c == 'u')
				value = p->n_users;
			else if (c == 'f')
				value = gkrellm_get_current_chartdata(p->forks_cd)
						/ fork_scaling;
			if (value >= 0)
				len = snprintf(buf, size, "%d", value);
			++s;
			}
		else
			*buf = *s;
		size -= len;
		buf += len;
		}
	*buf = '\0';
	}

static void
draw_proc_extra(void)
	{
	gchar		buf[128];

	if (!proc.chart || !proc.extra_info)
		return;
	format_proc_data(&proc, text_format_locale, buf, sizeof(buf));
	if (!new_text_format)
		gkrellm_chart_reuse_text_format(proc.chart);
	new_text_format = FALSE;
	gkrellm_draw_chart_text(proc.chart, style_id, buf);
	}

static void
cb_command_process(GkrellmAlert *alert, gchar *src, gchar *dst, gint len,
			gpointer data)
	{
	format_proc_data(&proc, src, dst, len);
	}

static void
refresh_proc_chart(GkrellmChart *cp)
	{
	if (proc.enabled)
		{
		gkrellm_draw_chartdata(cp);
		if (proc.extra_info)
			draw_proc_extra();
		gkrellm_draw_chart_to_screen(cp);
		}
	}

static void
draw_sensor_decals(void)
	{
	GkrellmPanel	*p    = proc.chart->panel;
	gchar			units;
	gfloat			t, f;
	gint			toggle;

	if (sensor_separate_mode && show_temperature && show_fan)
		{
		gkrellm_sensor_read_temperature(proc.sensor_temp, &t, &units);
		gkrellm_sensor_draw_temperature_decal(p, proc.sensor_decal, t, units);
		gkrellm_sensor_read_fan(proc.sensor_fan, &f);
		gkrellm_sensor_draw_fan_decal(p, proc.fan_decal, f);
		}
	else
		{
		toggle = _GK.time_now & 2;
		if (show_fan && (toggle || !show_temperature))
			{
			gkrellm_sensor_read_fan(proc.sensor_fan, &f);
			gkrellm_sensor_draw_fan_decal(p, proc.sensor_decal, f);
			}
		else if (show_temperature && (!toggle || !show_fan)
				)
			{
			gkrellm_sensor_read_temperature(proc.sensor_temp, &t, &units);
			gkrellm_sensor_draw_temperature_decal(p, proc.sensor_decal, t,
					units);
			}
		}
	}

void
gkrellm_proc_draw_sensors(gpointer sr)
	{
	if (sr && sr != proc.sensor_temp && sr != proc.sensor_fan)
		return;
	if (proc.enabled)
		draw_sensor_decals();
	}


static void
update_proc(void)
	{
	GkrellmChart	*cp;
	GkrellmPanel	*p;
	gint			load;

	if (!proc.enabled)
		return;
	(*read_proc_data)();
	if (GK.five_second_tick)
		{
		(*read_user_data)();
		gkrellm_check_alert(users_alert, proc.n_users);
		}
	cp = proc.chart;
	p = cp->panel;
	gkrellm_update_krell(p, KRELL(p), proc.n_forks);
	gkrellm_draw_panel_layers(p);

	if (GK.second_tick)
		{
		/* Scale load since it is a small real and charts are integer only.
		|  Scale the forks number by fork_scaling.  See setup_proc_scaling().
		*/
		load = (int) (LOAD_SCALING * proc.fload);
		gkrellm_store_chartdata(cp, 0, load, fork_scaling * proc.n_forks);
		refresh_proc_chart(cp);
		gkrellm_check_alert(load_alert, proc.fload);
		gkrellm_check_alert(processes_alert, proc.n_processes);
		gkrellm_panel_label_on_top_of_decals(p,
				gkrellm_alert_decal_visible(load_alert) ||
				gkrellm_alert_decal_visible(users_alert) ||
				gkrellm_alert_decal_visible(processes_alert));

		}
	if (   (GK.two_second_tick && !sensor_separate_mode)
		|| (GK.five_second_tick && sensor_separate_mode)
	   )
		draw_sensor_decals();
	}


static gint
proc_expose_event(GtkWidget *widget, GdkEventExpose *ev)
	{
	GkrellmChart	*cp		= proc.chart;
	GdkPixmap		*pixmap	= NULL;

	if (cp)
		{
		if (widget == cp->drawing_area)
			pixmap = cp->pixmap;
		else if (widget == cp->panel->drawing_area)
			pixmap = cp->panel->pixmap;
		}
	if (pixmap)
		gdk_draw_drawable(gtk_widget_get_window(widget), gkrellm_draw_GC(1), pixmap,
				  ev->area.x, ev->area.y, ev->area.x, ev->area.y,
				  ev->area.width, ev->area.height);
	return FALSE;
	}


static gint
cb_proc_extra(GtkWidget *widget, GdkEventButton *ev)
	{
	if (ev->button == 1 && ev->type == GDK_BUTTON_PRESS)
		{
		proc.extra_info = !proc.extra_info;
		gkrellm_config_modified();
		refresh_proc_chart(proc.chart);
		}
	else if (   ev->button == 3
			 || (ev->button == 1 && ev->type == GDK_2BUTTON_PRESS)
			)
		gkrellm_chartconfig_window_create(proc.chart);
	return FALSE;
	}

static void
setup_proc_scaling(GkrellmChartconfig *config, gpointer data)
	{
	GkrellmChart	*cp		= proc.chart;
	gint			grids, res, new_fork_scaling;

	if (!cp)
		return;

	grids = gkrellm_get_chartconfig_fixed_grids(cp->config);
	if (!grids)
		grids = FULL_SCALE_GRIDS;

	res = gkrellm_get_chartconfig_grid_resolution(cp->config);

	/* Since grid_resolution is set for load, set krell_full_scale explicitly
	|  to get what I want, which is 10 forks full scale.
	|  When res or number of grids is changed, scale all fork data to keep a
	|  fixed 50 forks/sec max on the chart.
	*/
	KRELL(cp->panel)->full_scale = 10;

	new_fork_scaling = grids * res / 50;
	if (new_fork_scaling < 1)		/* shouldn't happen... */
		new_fork_scaling = 1;

	/* If load grid_resolution changed, scale all fork data to keep the
	|  constant 50 forks/sec.
	*/
	if (fork_scaling != new_fork_scaling)
		{
		/* When called as a callback a chart refresh will follow, but I'll
		|  need a rescale here.
		*/
		gkrellm_scale_chartdata(proc.forks_cd, new_fork_scaling, fork_scaling);
		gkrellm_rescale_chart(proc.chart);
		}
	fork_scaling = new_fork_scaling;
	}

static void
destroy_proc_monitor(void)
	{
	GkrellmChart	*cp		= proc.chart;

	if (proc_launch.button)
		gkrellm_destroy_button(proc_launch.button);
	proc_launch.button = NULL;
	gkrellm_chart_destroy(cp);
	proc.chart = NULL;
	proc.enabled = FALSE;
	}

static void
cb_mb_temp_alert_trigger(GkrellmAlert *alert, ProcMon *monitor)
	{
	if (alert && monitor && monitor->chart)
		{
		GkrellmAlertdecal* ad = &alert->ad;
		GkrellmDecal* d = monitor->sensor_decal;
		if (d)
			{
			ad->x = d->x - 1;
			ad->y = d->y - 1;
			ad->w = d->w + 2;
			ad->h = d->h + 2;
			gkrellm_render_default_alert_decal(alert);
			}
		alert->panel = monitor->chart->panel;
		}
	}

static void
cb_mb_fan_alert_trigger(GkrellmAlert *alert, ProcMon *monitor)
	{
	if (alert && monitor && monitor->chart)
		{
		GkrellmAlertdecal* ad = &alert->ad;
		GkrellmDecal* d = sensor_separate_mode
			? monitor->fan_decal
			: monitor->sensor_decal;
		if (d)
			{
			ad->x = d->x - 1;
			ad->y = d->y - 1;
			ad->w = d->w + 2;
			ad->h = d->h + 2;
			gkrellm_render_default_alert_decal(alert);
			}
		alert->panel = monitor->chart->panel;
		}
	}

  /* Next routine is same as in cpu.c - perhaps should make into one?*/
  /* How to decide when to make the sensor_decal and fan_decal visible.
  |  The sensor_decal can show temp values, fan values, or alternating
  |  temp/fan values.  The fan_decal only shows fan values when non
  |  alternating mode is selected. The sensors are mapped in sensors.c
  |
  |   Sensor and fan decal display truth table:
  |   |-----decals visible----||--sensors mapped--|   separate  |
  |	  |sensor_decal  fan_decal||   temp     fan   |    mode     |
  |   |-----------------------||--------------------------------|
  |   |     0           0     ||     0       0          0       |
  |   |     1           0     ||     1       0          0       |
  |   |     1           0     ||     0       1          0       |
  |   |     1           0     ||     1       1          0       |
  |   |     0           0     ||     0       0          1       |
  |   |     1           0     ||     1       0          1       |
  |   |     1           0     ||     0       1          1       |
  |   |     1           1     ||     1       1          1       |
  |   |----------------------------------------------------------
  */
static gboolean
adjust_sensors_display(gint force)
	{
	GkrellmPanel	*p;
	GkrellmDecal	*ds, *df;
	GkrellmAlert	*alert;
	gint			position	= 0;

	ds = proc.sensor_decal;
	df = proc.fan_decal;
	if (!ds || !df)
		return FALSE;
	/* The test for state change is decal state vs success at reading
	|  a temperature.
	*/
	p = proc.chart->panel;
	show_temperature = show_fan = FALSE;
	if (!_GK.demo)
		{
		gkrellm_sensor_alert_connect(proc.sensor_temp,
					cb_mb_temp_alert_trigger, &proc);
		gkrellm_sensor_alert_connect(proc.sensor_fan,
					cb_mb_fan_alert_trigger, &proc);
		}

	/* If a fan alert is triggered, turn it off in case fan decal being used
	|  is changed.  The alert will just retrigger at next fan update.
	*/
	alert = gkrellm_sensor_alert(proc.sensor_fan);
	gkrellm_reset_alert_soft(alert);

	if (proc.sensor_temp || _GK.demo)
		show_temperature = TRUE;
	if (proc.sensor_fan || _GK.demo)
		show_fan = TRUE;

	if (show_temperature || show_fan)
		{
		if (! gkrellm_is_decal_visible(ds) || force)
			gkrellm_make_decal_visible(p, ds);
		position = 0;
		}
	else
		{
		if (gkrellm_is_decal_visible(ds) || force)
			gkrellm_make_decal_invisible(p, ds);
		position = proc.save_label_position;
		}
	if (show_fan && show_temperature && sensor_separate_mode)
		{
		if (! gkrellm_is_decal_visible(df) || force)
			gkrellm_make_decal_visible(p, df);
		position = -1;
		}
	else
		{
		if (gkrellm_is_decal_visible(df) || force)
			gkrellm_make_decal_invisible(p, df);
		}
	if (position != p->label->position || force)
		{
		if (proc.save_label_position >= 0)	/* Reassign position only if the */
			p->label->position = position;	/* original label was visible.   */
		gkrellm_draw_panel_label(p);
		draw_sensor_decals();
		gkrellm_draw_panel_layers(p);
		}
	return TRUE;
	}

static gint
cb_panel_press(GtkWidget *widget, GdkEventButton *ev)
	{
	if (ev->button == 3)
		gkrellm_open_config_window(mon_proc);
	return FALSE;
	}

static void
create_proc_monitor(GtkWidget *vbox, gint first_create)
	{
	GkrellmChart		*cp;
	GkrellmChartdata	*cd;
	GkrellmChartconfig	*cf;
	GkrellmPanel		*p;
	GkrellmStyle		*style;

	if (first_create)
		{
		proc.chart = gkrellm_chart_new0();
		proc.chart->panel = gkrellm_panel_new0();
		}
	cp = proc.chart;
	p = cp->panel;

	style = gkrellm_panel_style(style_id);
	gkrellm_create_krell(p, gkrellm_krell_panel_piximage(style_id), style);

	gkrellm_chart_create(proc.vbox, mon_proc, cp, &proc.chart_config);
	gkrellm_set_draw_chart_function(cp, refresh_proc_chart, proc.chart);
	cd = gkrellm_add_default_chartdata(cp, _("Load"));
	gkrellm_monotonic_chartdata(cd, FALSE);
	gkrellm_set_chartdata_draw_style_default(cd, CHARTDATA_LINE);
	proc.forks_cd = gkrellm_add_default_chartdata(cp, _("Forks"));
	gkrellm_set_chartdata_flags(proc.forks_cd, CHARTDATA_ALLOW_HIDE);

	cf = cp->config;
	gkrellm_chartconfig_fixed_grids_connect(cf, setup_proc_scaling, NULL);
	gkrellm_chartconfig_grid_resolution_connect(cf, setup_proc_scaling, NULL);
	gkrellm_chartconfig_grid_resolution_adjustment(cf, FALSE,
			LOAD_SCALING, 0.5, 5.0, 0.5, 0.5, 1, 50);
	gkrellm_chartconfig_grid_resolution_label(cf,
					_("Average process load per minute"));

	gkrellm_alloc_chartdata(cp);
	setup_proc_scaling(NULL, NULL);

	/* I put motherboard temp on Proc panel (if temperature sensors found)
	*/
	gkrellm_sensors_create_decals(p, style_id,
					&proc.sensor_decal, &proc.fan_decal);

	gkrellm_panel_configure(p, proc.panel_label, style);

	gkrellm_panel_create(proc.vbox, mon_proc, p);

	proc.save_label_position = p->label->position;
	if (proc.sensor_decal)
		adjust_sensors_display(TRUE);

	new_text_format = TRUE;
	if (first_create)
		{
		g_signal_connect(G_OBJECT(cp->drawing_area), "expose-event",
				G_CALLBACK(proc_expose_event), NULL);
		g_signal_connect(G_OBJECT(p->drawing_area), "expose-event",
				G_CALLBACK(proc_expose_event), NULL);
		g_signal_connect(G_OBJECT(cp->drawing_area),"button-press-event",
				G_CALLBACK(cb_proc_extra), NULL);
		g_signal_connect(G_OBJECT(p->drawing_area), "button-press-event",
				G_CALLBACK(cb_panel_press), NULL);
		}
	else
		refresh_proc_chart(cp);
	gkrellm_setup_launcher(p, &proc_launch, CHART_PANEL_TYPE, 4);
	}

static void
create_proc(GtkWidget *vbox, gint first_create)
	{
	proc.vbox = vbox;
	if (proc.enabled)
		{
		create_proc_monitor(proc.vbox, first_create);
		gkrellm_spacers_show(mon_proc);
		}
	else
		gkrellm_spacers_hide(mon_proc);
	}



#define	PROC_CONFIG_KEYWORD	"proc"

static void
cb_alert_trigger(GkrellmAlert *alert, gpointer data)
	{
	GkrellmPanel		*p;
	GkrellmAlertdecal   *ad;
	GkrellmDecal        *ds, *df;
	gint				x, w;

	p = proc.chart->panel;
	alert->panel = p;
	ds = proc.sensor_decal;
	df = proc.fan_decal;
	ad = &alert->ad;
	if (gkrellm_is_decal_visible(ds) && !gkrellm_is_decal_visible(df))
		w = ds->x - 1;
	else
		w = p->w;
	w /= 3;
	if (w < 2)
		w = 2;
	if (alert == load_alert)
		x = 0;
	else if (alert == users_alert)
		x = w;
	else
		x = 2 * w;
	ad->x = x;
	ad->y = 0;
	ad->w = w;
	ad->h = p->h;
	gkrellm_render_default_alert_decal(alert);
	}

static void
create_load_alert(void)
	{
	load_alert = gkrellm_alert_create(NULL, _("Load"),
			NULL,
			TRUE, FALSE, TRUE,
			20, 1, 0.5, 1, 2);
	gkrellm_alert_delay_config(load_alert, 1, 10000, 0);

	gkrellm_alert_trigger_connect(load_alert, cb_alert_trigger, NULL);
	gkrellm_alert_command_process_connect(load_alert,
			cb_command_process, NULL);
	}

static void
create_users_alert(void)
	{
	users_alert = gkrellm_alert_create(NULL, _("Users"),
			NULL,
			TRUE, FALSE, TRUE,
			100000, 2, 1, 10, 0);
	gkrellm_alert_trigger_connect(users_alert, cb_alert_trigger, NULL);
	gkrellm_alert_command_process_connect(users_alert,
			cb_command_process, NULL);
	}

static void
create_processes_alert(void)
	{
	processes_alert = gkrellm_alert_create(NULL, _("Processes"),
			NULL,
			TRUE, FALSE, TRUE,
			100000, 10, 1, 10, 0);
	gkrellm_alert_trigger_connect(processes_alert, cb_alert_trigger, NULL);
	gkrellm_alert_command_process_connect(processes_alert,
			cb_command_process, NULL);
	}

static void
save_proc_config(FILE *f)
	{
	fprintf(f, "%s enable %d %d\n", PROC_CONFIG_KEYWORD,
				proc.enabled, proc.extra_info);
	fprintf(f, "%s launch %s\n", PROC_CONFIG_KEYWORD, proc_launch.command);
	fprintf(f, "%s tooltip_comment %s\n", PROC_CONFIG_KEYWORD,
				proc_launch.tooltip_comment);
	fprintf(f, "%s sensor_mode %d\n", PROC_CONFIG_KEYWORD,
				sensor_separate_mode);
	fprintf(f, "%s text_format %s\n", PROC_CONFIG_KEYWORD, text_format);
	gkrellm_save_chartconfig(f, proc.chart_config,
				PROC_CONFIG_KEYWORD, NULL);
	if (load_alert)
		gkrellm_save_alertconfig(f, load_alert, PROC_CONFIG_KEYWORD, "load");
	if (users_alert)
		gkrellm_save_alertconfig(f, users_alert, PROC_CONFIG_KEYWORD, "users");
	if (processes_alert)
		gkrellm_save_alertconfig(f, processes_alert,
					PROC_CONFIG_KEYWORD, "processes");
	}

static void
load_proc_config(gchar *arg)
	{
	gchar	config[32], name[16], item[CFG_BUFSIZE], item1[CFG_BUFSIZE];
	gint	n;

	n = sscanf(arg, "%31s %[^\n]", config, item);
	if (n == 2)
		{
		if (!strcmp(config, "enable"))
			sscanf(item, "%d %d\n", &proc.enabled, &proc.extra_info);
		else if (!strcmp(config, "launch"))
			proc_launch.command = g_strdup(item);
		else if (!strcmp(config, "tooltip_comment"))
			proc_launch.tooltip_comment = g_strdup(item);
		else if (!strcmp(config, "sensor_mode"))
			sscanf(item, "%d\n", &sensor_separate_mode);
		else if (!strcmp(config, "text_format"))
			{
			gkrellm_locale_dup_string(&text_format, item, &text_format_locale);
			new_text_format = TRUE;
			}
		else if (!strcmp(config, GKRELLM_CHARTCONFIG_KEYWORD))
			gkrellm_load_chartconfig(&proc.chart_config, item, 2);
		else if (!strcmp(config, GKRELLM_ALERTCONFIG_KEYWORD))
			{
			if (sscanf(item, "%15s %[^\n]", name, item1) == 2)
				{
				if (!strcmp(name, "load"))
					{
					if (!load_alert)
						create_load_alert();
					gkrellm_load_alertconfig(&load_alert, item1);
					}
				else if (!strcmp(name, "users"))
					{
					if (!users_alert)
						create_users_alert();
					gkrellm_load_alertconfig(&users_alert, item1);
					}
				else if (!strcmp(name, "processes"))
					{
					if (!processes_alert)
						create_processes_alert();
					gkrellm_load_alertconfig(&processes_alert, item1);
					}
				}
			}
		}
	}

/* ---------------------------------------------------------------------- */
#define DEFAULT_TEXT_FORMAT	"\\w88\\a$p\\f procs\\n\\e$u\\f users"


static GtkWidget	*proc_launch_entry,
					*proc_tooltip_entry;

static GtkWidget	*text_format_combo_box;

static GtkWidget	*load_alert_button,
					*users_alert_button,
					*processes_alert_button;


static gboolean
fix_panel(void)
	{
	gboolean	result;

	if (!proc.enabled)
		return FALSE;
	if ((result = adjust_sensors_display(FALSE)) && proc_launch.button)
		{
		gkrellm_destroy_button(proc_launch.button);
		proc_launch.button =
			gkrellm_put_label_in_panel_button(proc.chart->panel,
				gkrellm_launch_button_cb, &proc_launch, proc_launch.pad);
		}
	return result;
	}

gboolean
gkrellm_proc_set_sensor(gpointer sr, gint type)
	{
	if (type == SENSOR_TEMPERATURE)
		proc.sensor_temp = sr;
	else if (type == SENSOR_FAN)
		proc.sensor_fan = sr;
	else
		return FALSE;
	return fix_panel();
	}

static void
cb_sensor_separate(GtkWidget *button, gpointer data)
	{
	sensor_separate_mode = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button));
	fix_panel();
	}

static void
cb_text_format(GtkWidget *widget, gpointer data)
	{
	gchar		*s;
	GtkWidget	*entry;

	entry = gtk_bin_get_child(GTK_BIN(text_format_combo_box));
	s = gkrellm_gtk_entry_get_text(&entry);
	gkrellm_locale_dup_string(&text_format, s, &text_format_locale);
	new_text_format = TRUE;
	refresh_proc_chart(proc.chart);
	}

static void
cb_enable(GtkWidget *button, gpointer data)
    {
	gboolean enabled;

	enabled = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button));
	if (enabled && ! proc.enabled)
		{
		create_proc_monitor(proc.vbox, TRUE);
		gkrellm_spacers_show(mon_proc);
		}
	else if (!enabled && proc.enabled)
		{
		destroy_proc_monitor();
		gkrellm_spacers_hide(mon_proc);
		}
	proc.enabled = enabled;

	gtk_widget_set_sensitive(load_alert_button, enabled);
	gtk_widget_set_sensitive(users_alert_button, enabled);
	gtk_widget_set_sensitive(processes_alert_button, enabled);
	}

static void
cb_load_alert(GtkWidget *button, gpointer data)
	{
	if (!load_alert)
		create_load_alert();
	gkrellm_alert_config_window(&load_alert);
	}

static void
cb_users_alert(GtkWidget *button, gpointer data)
	{
	if (!users_alert)
		create_users_alert();
	gkrellm_alert_config_window(&users_alert);
	}

static void
cb_processes_alert(GtkWidget *button, gpointer data)
	{
	if (!processes_alert)
		create_processes_alert();
	gkrellm_alert_config_window(&processes_alert);
	}

static void
cb_launch_entry(GtkWidget *widget, gpointer data)
	{
	if (proc.enabled)
		{
		gkrellm_apply_launcher(&proc_launch_entry, &proc_tooltip_entry,
					proc.chart->panel, &proc_launch, gkrellm_launch_button_cb);
		}
	}


static gchar	*proc_info_text[] =
{
N_("<h>Proc Chart"),
"\n",
N_("The krell shows process forks with a full scale value of 10 forks.\n"),
N_("While both load and fork data are drawn on the chart, the grid\n"
"resolution can be set for load only.  The resolution per grid for forks is\n"
"fixed at 10 when using the auto number of grids mode, and at 50 divided by\n"
"the number of grids when using a fixed number of grids mode.\n"),
"\n",
N_("<h>Chart Labels\n"),
N_("Substitution variables for the format string for chart labels:\n"),
N_("\t$L    maximum chart value (load)\n"),
N_("\t$F    maximum chart value (forks)\n"),
N_("\t$l    load\n"),
N_("\t$f    forks\n"),
N_("\t$p    processes\n"),
N_("\t$u    users\n"),
"\n",
N_("Substitution variables may be used in alert commands.\n")
};

static void
create_proc_tab(GtkWidget *tab_vbox)
	{
	GtkWidget	*tabs, *table, *vbox, *vbox1, *hbox, *text, *label;

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

/* ---Options tab */
	vbox = gkrellm_gtk_framed_notebook_page(tabs, _("Options"));
	gkrellm_gtk_check_button_connected(vbox, NULL,
				proc.enabled, FALSE, FALSE, 4,
				cb_enable, NULL,
				_("Enable Proc chart"));

	if (gkrellm_sensors_available())
		gkrellm_gtk_check_button_connected(vbox, NULL,
					sensor_separate_mode, FALSE, FALSE, 0,
					cb_sensor_separate, NULL,
		_("Draw fan and temperature values separately (not alternating)."));

	hbox = gtk_hbox_new(FALSE, 0);
	gtk_box_pack_end(GTK_BOX(vbox), hbox, FALSE, TRUE, 5);
	gkrellm_gtk_alert_button(hbox, &processes_alert_button, FALSE, FALSE, 4,
				TRUE, cb_processes_alert, NULL);
	label = gtk_label_new(_("Processes"));
	gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, TRUE, 4);

	hbox = gtk_hbox_new(FALSE, 0);
	gtk_box_pack_end(GTK_BOX(vbox), hbox, FALSE, TRUE, 5);
	gkrellm_gtk_alert_button(hbox, &users_alert_button, FALSE, FALSE, 4, TRUE,
				cb_users_alert, NULL);
	label = gtk_label_new(_("Users"));
	gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, TRUE, 4);

	hbox = gtk_hbox_new(FALSE, 0);
	gtk_box_pack_end(GTK_BOX(vbox), hbox, FALSE, TRUE, 5);
	gkrellm_gtk_alert_button(hbox, &load_alert_button, FALSE, FALSE, 4, TRUE,
				cb_load_alert, NULL);
	label = gtk_label_new(_("Load"));
	gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, TRUE, 4);

	if (!proc.enabled)
		{
		gtk_widget_set_sensitive(load_alert_button, FALSE);
		gtk_widget_set_sensitive(users_alert_button, FALSE);
		gtk_widget_set_sensitive(processes_alert_button, FALSE);
		}

	vbox = gkrellm_gtk_framed_notebook_page(tabs, _("Setup"));

	vbox1 = gkrellm_gtk_category_vbox(vbox,
				_("Format String for Chart Labels"),
				4, 0, TRUE);

	text_format_combo_box = gtk_combo_box_text_new_with_entry();
	gtk_widget_set_size_request (GTK_WIDGET(text_format_combo_box), 350, -1);
	gtk_box_pack_start(GTK_BOX(vbox1), text_format_combo_box, FALSE, FALSE, 2);
	gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(text_format_combo_box), text_format);
	gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(text_format_combo_box),
			_(DEFAULT_TEXT_FORMAT));
	gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(text_format_combo_box),
			_("\\f$L\\r\\f$F \\w88\\b\\p\\a$p\\f procs\\n\\e$u\\f users"));
	gtk_combo_box_set_active(GTK_COMBO_BOX(text_format_combo_box), 0);
	g_signal_connect(G_OBJECT(text_format_combo_box), "changed",
			G_CALLBACK(cb_text_format), NULL);

	vbox1 = gkrellm_gtk_category_vbox(vbox,
				_("Launch Commands"),
				4, 0, TRUE);
	table = gkrellm_gtk_launcher_table_new(vbox1, 1);
	gkrellm_gtk_config_launcher(table, 0,
				&proc_launch_entry, &proc_tooltip_entry,
				_("Proc"), &proc_launch);
	g_signal_connect(G_OBJECT(proc_launch_entry), "changed",
				G_CALLBACK(cb_launch_entry), NULL);
	g_signal_connect(G_OBJECT(proc_tooltip_entry), "changed",
				G_CALLBACK(cb_launch_entry), NULL);

/* --Info tab */
	vbox = gkrellm_gtk_framed_notebook_page(tabs, _("Info"));
	text = gkrellm_gtk_scrolled_text_view(vbox, NULL,
				GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
	for (size_t i = 0; i < G_N_ELEMENTS(proc_info_text); ++i)
		gkrellm_gtk_text_view_append(text, _(proc_info_text[i]));
	}


gchar *
gkrellm_proc_get_sensor_panel_label(void)
	{
	return proc.panel_label;
	}

GkrellmMonitor *
gkrellm_get_proc_mon(void)
	{
	return mon_proc;
	}

static GkrellmMonitor	monitor_proc =
	{
	N_("Proc"),			/* Name, for config tab.	*/
	MON_PROC,			/* Id,  0 if a plugin		*/
	create_proc,		/* The create function		*/
	update_proc,		/* The update function		*/
	create_proc_tab,	/* The config tab create function	*/
	NULL,				/* Instant apply */

	save_proc_config,	/* Save user config			*/
	load_proc_config,	/* Load user config			*/
	PROC_CONFIG_KEYWORD, /* config keyword			*/

	NULL,				/* Undef 2	*/
	NULL,				/* Undef 1	*/
	NULL,				/* Undef 0	*/

	0,					/* insert_before_id - place plugin before this mon */

	NULL,				/* Handle if a plugin, filled in by GKrellM		*/
	NULL				/* path if a plugin, filled in by GKrellM		*/
	};

GkrellmMonitor *
gkrellm_init_proc_monitor(void)
	{
	GkrellmChartconfig	*cf;

	monitor_proc.name = _(monitor_proc.name);
	proc.panel_label = g_strdup(_("Proc"));
	proc.enabled = TRUE;
	proc.extra_info = TRUE;
	style_id = gkrellm_add_chart_style(&monitor_proc, PROC_STYLE_NAME);
	gkrellm_locale_dup_string(&text_format, _(DEFAULT_TEXT_FORMAT),
				&text_format_locale);

	mon_proc = &monitor_proc;
	if (setup_proc_interface())
		{
		/* Set chart config defaults.  Turn off auto grid resolution and
		|  don't let user config it back on.
		*/
		cf = proc.chart_config = gkrellm_chartconfig_new0();
		gkrellm_set_chartconfig_grid_resolution(cf, 100);
		gkrellm_set_chartconfig_auto_grid_resolution(cf, FALSE);
		gkrellm_set_chartconfig_flags(cf, NO_CONFIG_AUTO_GRID_RESOLUTION);

		return &monitor_proc;
		}
	return NULL;
	}

