changeset 9908:4b4177fc63d9

6834110 PowerTOP's display.c needs some lovin'
author Rafael Vanoni <rafael.vanoni@sun.com>
date Fri, 19 Jun 2009 06:14:38 -0700
parents 98086c85a8f7
children aa280f585a3e
files usr/src/cmd/powertop/amd64/pt_amd64.c usr/src/cmd/powertop/common/battery.c usr/src/cmd/powertop/common/cpufreq.c usr/src/cmd/powertop/common/display.c usr/src/cmd/powertop/common/powertop.c usr/src/cmd/powertop/common/powertop.h usr/src/cmd/powertop/common/suggestions.c usr/src/cmd/powertop/i386/pt_i386.c usr/src/cmd/powertop/sparcv9/pt_sparcv9.c
diffstat 9 files changed, 754 insertions(+), 492 deletions(-) [+]
line wrap: on
line diff
--- a/usr/src/cmd/powertop/amd64/pt_amd64.c	Fri Jun 19 20:12:07 2009 +0800
+++ b/usr/src/cmd/powertop/amd64/pt_amd64.c	Fri Jun 19 06:14:38 2009 -0700
@@ -217,3 +217,4 @@
  */
 const char *g_msg_idle_state = "C-states (idle power)";
 const char *g_msg_freq_state = "P-states (frequencies)";
+const char *g_msg_freq_enable = "P - Enable P-states";
--- a/usr/src/cmd/powertop/common/battery.c	Fri Jun 19 20:12:07 2009 +0800
+++ b/usr/src/cmd/powertop/common/battery.c	Fri Jun 19 06:14:38 2009 -0700
@@ -80,7 +80,7 @@
 }
 
 void
-print_battery(void)
+pt_battery_print(void)
 {
 	int err;
 
@@ -105,7 +105,7 @@
 			    err);
 	}
 
-	show_acpi_power_line(battery_state.exist, battery_state.present_rate,
+	pt_display_acpi_power(battery_state.exist, battery_state.present_rate,
 	    battery_state.remain_cap, battery_state.last_cap,
 	    battery_state.bst_state);
 }
--- a/usr/src/cmd/powertop/common/cpufreq.c	Fri Jun 19 20:12:07 2009 +0800
+++ b/usr/src/cmd/powertop/common/cpufreq.c	Fri Jun 19 06:14:38 2009 -0700
@@ -54,12 +54,12 @@
 
 /*
  * Enabling PM through /etc/power.conf
- * See suggest_p_state()
+ * See pt_cpufreq_suggest()
  */
 static char default_conf[]	= "/etc/power.conf";
 static char default_pmconf[]	= "/usr/sbin/pmconfig";
-static char cpupm_enable[]	= " echo cpupm enable >> /etc/power.conf";
-static char cpupm_treshold[]	= " echo cpu-threshold 1s >> /etc/power.conf";
+static char cpupm_enable[]	= "echo cpupm enable >> /etc/power.conf";
+static char cpupm_treshold[]	= "echo cpu-threshold 1s >> /etc/power.conf";
 
 /*
  * Buffer containing DTrace program to track CPU frequency transitions
@@ -123,8 +123,9 @@
 static int	pt_cpufreq_snapshot(void);
 static int	pt_cpufreq_dtrace_walk(const dtrace_aggdata_t *, void *);
 static void	pt_cpufreq_stat_account(double, uint_t);
-static int	pt_cpufreq_snapshot_cpu(kstat_ctl_t *,
-    uint_t);
+static int	pt_cpufreq_snapshot_cpu(kstat_ctl_t *, uint_t);
+static int	pt_cpufreq_check_pm(void);
+static void	pt_cpufreq_enable(void);
 
 static int
 pt_cpufreq_setup(void)
@@ -499,51 +500,74 @@
 }
 
 /*
+ * Checks if PM is enabled in /etc/power.conf, enabling if not
+ */
+void
+pt_cpufreq_suggest(void)
+{
+	int ret = pt_cpufreq_check_pm();
+
+	switch (ret) {
+	case 0:
+		pt_sugg_add("Suggestion: enable CPU power management by "
+		    "pressing the P key", 40, 'P', (char *)g_msg_freq_enable,
+		    pt_cpufreq_enable);
+		break;
+	}
+}
+
+/*
+ * Checks /etc/power.conf and returns:
+ *
+ *     0 if CPUPM is not enabled
+ *     1 if there's nothing for us to do because:
+ *         (a) the system does not support frequency scaling
+ *         (b) there's no power.conf.
+ *     2 if CPUPM is enabled
+ *     3 if the system is running in poll-mode, as opposed to event-mode
+ *
+ * Notice the ordering of the return values, they will be picked up and
+ * switched upon ascendingly.
+ */
+static int
+pt_cpufreq_check_pm(void)
+{
+	char line[1024];
+	FILE *file;
+	int ret = 0;
+
+	if (g_npstates < 2 || (file = fopen(default_conf, "r")) == NULL)
+		return (1);
+
+	(void) memset(line, 0, 1024);
+
+	while (fgets(line, 1024, file)) {
+		if (strstr(line, "cpupm")) {
+			if (strstr(line, "enable")) {
+				(void) fclose(file);
+				return (2);
+			}
+		}
+		if (strstr(line, "poll"))
+			ret = 3;
+	}
+
+	(void) fclose(file);
+
+	return (ret);
+}
+
+/*
  * Used as a suggestion, sets PM in /etc/power.conf and
  * a 1sec threshold, then calls /usr/sbin/pmconfig
  */
-void
-enable_p_state(void)
+static void
+pt_cpufreq_enable(void)
 {
 	(void) system(cpupm_enable);
 	(void) system(cpupm_treshold);
 	(void) system(default_pmconf);
+
+	if (pt_sugg_remove(pt_cpufreq_enable) == 0)
+		pt_error("%s : failed to remove a sugg.\n", __FILE__);
 }
-
-/*
- * Checks if PM is enabled in /etc/power.conf, enabling if not
- */
-void
-suggest_p_state(void)
-{
-	char 	line[1024];
-	FILE 	*file;
-
-	/*
-	 * Return if speed transition is not supported
-	 */
-	if (g_npstates < 2)
-		return;
-
-	file = fopen(default_conf, "r");
-
-	if (!file)
-		return;
-
-	(void) memset(line, 0, 1024);
-
-	while (fgets(line, 1023, file)) {
-		if (strstr(line, "cpupm")) {
-			if (strstr(line, "enable")) {
-				(void) fclose(file);
-				return;
-			}
-		}
-	}
-
-	add_suggestion("Suggestion: enable CPU power management by "
-	    "pressing the P key",  40, 'P', "P - Enable p-state",
-	    enable_p_state);
-
-	(void) fclose(file);
-}
--- a/usr/src/cmd/powertop/common/display.c	Fri Jun 19 20:12:07 2009 +0800
+++ b/usr/src/cmd/powertop/common/display.c	Fri Jun 19 06:14:38 2009 -0700
@@ -39,16 +39,38 @@
 
 #include <stdlib.h>
 #include <string.h>
+#include <unistd.h>
 #include <curses.h>
+#include <signal.h>
+#include <fcntl.h>
 #include "powertop.h"
 
-static WINDOW 	*title_bar_window;
-static WINDOW 	*cstate_window;
-static WINDOW 	*wakeup_window;
-static WINDOW 	*acpi_power_window;
-static WINDOW 	*eventstat_window;
-static WINDOW 	*suggestion_window;
-static WINDOW 	*status_bar_window;
+/*
+ * Minimum terminal height and width to run PowerTOP on curses mode.
+ */
+#define	PT_MIN_COLS		70
+#define	PT_MIN_ROWS		15
+
+/*
+ * Display colors
+ */
+#define	PT_COLOR_DEFAULT	1
+#define	PT_COLOR_HEADER_BAR	2
+#define	PT_COLOR_ERROR		3
+#define	PT_COLOR_RED		4
+#define	PT_COLOR_YELLOW		5
+#define	PT_COLOR_GREEN		6
+#define	PT_COLOR_BRIGHT		7
+#define	PT_COLOR_BLUE		8
+
+/*
+ * Constants for pt_display_setup()
+ */
+#define	SINGLE_LINE_SW 		1
+#define	LENGTH_SUGG_SW		2
+#define	TITLE_LINE		1
+#define	BLANK_LINE		1
+#define	NEXT_LINE		1
 
 #define	print(win, y, x, fmt, args...)				\
 	if (PT_ON_DUMP)						\
@@ -56,48 +78,73 @@
 	else							\
 		(void) mvwprintw(win, y, x, fmt, ## args);
 
-char 		g_status_bar_slots[PT_BAR_NSLOTS][PT_BAR_LENGTH];
-char 		g_suggestion_key;
+enum pt_subwindows {
+	SW_TITLE,
+	SW_IDLE,
+	SW_FREQ,
+	SW_WAKEUPS,
+	SW_POWER,
+	SW_EVENTS,
+	SW_SUGG,
+	SW_STATUS,
+	SW_COUNT
+};
 
-static int	maxx, maxy;
+typedef struct sb_slot {
+	char *msg;
+	struct sb_slot *prev;
+	struct sb_slot *next;
+} sb_slot_t;
+
+static WINDOW *sw[SW_COUNT];
+static int win_cols, win_rows;
+static sb_slot_t *status_bar;
 
 static void
-zap_windows(void)
+pt_display_cleanup(void)
+{
+	(void) endwin();
+}
+
+static void
+pt_display_get_size(void)
 {
-	if (title_bar_window) {
-		(void) delwin(title_bar_window);
-		title_bar_window = NULL;
-	}
-	if (cstate_window) {
-		(void) delwin(cstate_window);
-		cstate_window = NULL;
-	}
-	if (wakeup_window) {
-		(void) delwin(wakeup_window);
-		wakeup_window = NULL;
-	}
-	if (acpi_power_window) {
-		(void) delwin(acpi_power_window);
-		acpi_power_window = NULL;
-	}
-	if (eventstat_window) {
-		(void) delwin(eventstat_window);
-		eventstat_window = NULL;
-	}
-	if (suggestion_window) {
-		(void) delwin(suggestion_window);
-		suggestion_window = NULL;
-	}
-	if (status_bar_window) {
-		(void) delwin(status_bar_window);
-		status_bar_window = NULL;
+	getmaxyx(stdscr, win_rows, win_cols);
+
+	if (win_rows < PT_MIN_ROWS || win_cols < PT_MIN_COLS) {
+		pt_display_cleanup();
+		(void) printf("\n\nPowerTOP cannot run in such a small "
+		    "terminal window. Please resize it.\n\n");
+		exit(EXIT_FAILURE);
 	}
 }
 
-void
-cleanup_curses(void)
+/*
+ * Signal handler, currently only used for window resizing.
+ */
+static void
+pt_display_resize(int sig)
 {
-	(void) endwin();
+	int i;
+
+	switch (sig) {
+	case SIGWINCH:
+		for (i = 0; i < SW_COUNT; i++)
+			if (sw[i] != NULL) {
+				(void) delwin(sw[i]);
+				sw[i] = NULL;
+			}
+
+		pt_display_cleanup();
+		(void) pt_display_init_curses();
+		pt_display_setup(B_TRUE);
+
+		pt_display_title_bar();
+
+		pt_display_update();
+
+		break;
+	}
 }
 
 /*
@@ -108,55 +155,75 @@
  * 	subwin(WINDOW *orig, int nlines, int ncols, int begin_y, int begin_x)
  */
 void
-setup_windows(void)
+pt_display_setup(boolean_t resized)
 {
 	/*
 	 * These variables are used to properly set the initial y position and
 	 * number of lines in each subwindow, as the number of supported CPU
 	 * states affects their placement.
 	 */
-	int cstate_lines, event_lines, pos_y;
-
-	getmaxyx(stdscr, maxy, maxx);
+	int cstate_lines, event_lines, pos_y = 0;
 
-	zap_windows();
+	/*
+	 * In theory, all systems have at least two idle states. We add two here
+	 * since we have to use DTrace to figure out how many this box has.
+	 */
+	cstate_lines = TITLE_LINE + max((g_max_cstate+2), g_npstates);
 
-	cstate_lines 	= TITLE_LINE + max((g_max_cstate+1), g_npstates);
-
-	pos_y = 0;
-	title_bar_window = subwin(stdscr, SINGLE_LINE_SW, maxx, pos_y, 0);
+	sw[SW_TITLE] = subwin(stdscr, SINGLE_LINE_SW, win_cols, pos_y, 0);
 
 	pos_y += NEXT_LINE + BLANK_LINE;
-	cstate_window = subwin(stdscr, cstate_lines, maxx, pos_y, 0);
+	sw[SW_IDLE] = subwin(stdscr, cstate_lines, win_cols/2 + 1, pos_y, 0);
+	sw[SW_FREQ] = subwin(stdscr, cstate_lines, win_cols/2 - 8, pos_y,
+	    win_cols/2 + 8);
 
 	pos_y += cstate_lines + BLANK_LINE;
-	wakeup_window = subwin(stdscr, SINGLE_LINE_SW, maxx, pos_y, 0);
+	sw[SW_WAKEUPS] = subwin(stdscr, SINGLE_LINE_SW, win_cols, pos_y, 0);
 
 	pos_y += NEXT_LINE;
-	acpi_power_window = subwin(stdscr, SINGLE_LINE_SW, maxx, pos_y, 0);
+	sw[SW_POWER] = subwin(stdscr, SINGLE_LINE_SW, win_cols, pos_y, 0);
 
 	pos_y += NEXT_LINE + BLANK_LINE;
-	event_lines = maxy - SINGLE_LINE_SW - NEXT_LINE - LENGTH_SUGG_SW -
+	event_lines = win_rows - SINGLE_LINE_SW - NEXT_LINE - LENGTH_SUGG_SW -
 	    pos_y;
-	eventstat_window = subwin(stdscr, event_lines, maxx, pos_y, 0);
+
+	if (event_lines > 0) {
+		sw[SW_EVENTS] = subwin(stdscr, event_lines, win_cols, pos_y, 0);
+	} else {
+		(void) printf("\n\nPowerTOP cannot run in such a small "
+		    "terminal window, please resize it.\n\n");
+		exit(EXIT_FAILURE);
+	}
 
 	pos_y += event_lines + NEXT_LINE;
-	suggestion_window = subwin(stdscr, SINGLE_LINE_SW, maxx, pos_y, 0);
+	sw[SW_SUGG] = subwin(stdscr, SINGLE_LINE_SW, win_cols, pos_y, 0);
 
 	pos_y += BLANK_LINE + NEXT_LINE;
-	status_bar_window = subwin(stdscr, SINGLE_LINE_SW, maxx, pos_y, 0);
+	sw[SW_STATUS] = subwin(stdscr, SINGLE_LINE_SW, win_cols, pos_y, 0);
+
+	if (!resized) {
+		status_bar = NULL;
 
-	(void) strcpy(g_status_bar_slots[0], _(" Q - Quit "));
-	(void) strcpy(g_status_bar_slots[1], _(" R - Refresh "));
+		pt_display_mod_status_bar(_("Q - Quit"));
+		pt_display_mod_status_bar(_("R - Refresh"));
+	}
 
-	(void) werase(stdscr);
-	(void) wrefresh(status_bar_window);
+	pt_display_status_bar();
 }
 
+/*
+ * This routine handles all the necessary curses initialization.
+ */
 void
-initialize_curses(void)
+pt_display_init_curses(void)
 {
 	(void) initscr();
+
+	(void) atexit(pt_display_cleanup);
+	(void) signal(SIGWINCH, pt_display_resize);
+
+	pt_display_get_size();
+
 	(void) start_color();
 
 	/*
@@ -192,86 +259,172 @@
 	(void) init_pair(PT_COLOR_GREEN, COLOR_WHITE, COLOR_GREEN);
 	(void) init_pair(PT_COLOR_BLUE, COLOR_WHITE, COLOR_BLUE);
 	(void) init_pair(PT_COLOR_BRIGHT, COLOR_WHITE, COLOR_BLACK);
+}
 
-	(void) atexit(cleanup_curses);
+void
+pt_display_update(void)
+{
+	(void) doupdate();
+}
+
+void
+pt_display_title_bar(void)
+{
+	char	title_pad[10];
+
+	(void) wattrset(sw[SW_TITLE], COLOR_PAIR(PT_COLOR_HEADER_BAR));
+	(void) wbkgd(sw[SW_TITLE], COLOR_PAIR(PT_COLOR_HEADER_BAR));
+	(void) werase(sw[SW_TITLE]);
+
+	(void) snprintf(title_pad, 10, "%%%ds",
+	    (win_cols - strlen(TITLE))/2 + strlen(TITLE));
+
+	/* LINTED: E_SEC_PRINTF_VAR_FMT */
+	print(sw[SW_TITLE], 0, 0, title_pad, TITLE);
+
+	(void) wnoutrefresh(sw[SW_TITLE]);
 }
 
 void
-show_title_bar(void)
+pt_display_status_bar(void)
 {
-	int 	i, x = 0, y = 0;
-	char	title_pad[10];
+	sb_slot_t *n = status_bar;
+	int x = 0;
+
+	(void) werase(sw[SW_STATUS]);
+
+	while (n && x < win_cols) {
+		(void) wattron(sw[SW_STATUS], A_REVERSE);
+		print(sw[SW_STATUS], 0, x, "%s", n->msg);
+		(void) wattroff(sw[SW_STATUS], A_REVERSE);
+		x += strlen(n->msg) + 1;
+
+		n = n->next;
+	}
+
+	(void) wnoutrefresh(sw[SW_STATUS]);
+}
 
-	(void) wattrset(title_bar_window, COLOR_PAIR(PT_COLOR_HEADER_BAR));
-	(void) wbkgd(title_bar_window, COLOR_PAIR(PT_COLOR_HEADER_BAR));
-	(void) werase(title_bar_window);
+/*
+ * Adds or removes items to the status bar automatically.
+ * Only one instance of an item allowed.
+ */
+void
+pt_display_mod_status_bar(char *msg)
+{
+	sb_slot_t *new, *n;
+	boolean_t found = B_FALSE, first = B_FALSE;
+
+	if (msg == NULL) {
+		pt_error("%s : can't add an empty status bar item.", __FILE__);
+		return;
+	}
+
+	if (status_bar != NULL) {
+		/*
+		 * Non-empty status bar. Look for an entry matching this msg.
+		 */
+		for (n = status_bar; n != NULL; n = n->next) {
+
+			if (strcmp(msg, n->msg) == 0) {
+				if (n != status_bar)
+					n->prev->next = n->next;
+				else
+					first = B_TRUE;
 
-	(void) snprintf(title_pad, 10, "%%%ds",
-	    (maxx - strlen(TITLE))/2 + strlen(TITLE));
-	/* LINTED: E_SEC_PRINTF_VAR_FMT */
-	print(title_bar_window, y, x, title_pad, TITLE);
+				if (n->next != NULL) {
+					n->next->prev = n->prev;
+					if (first)
+						status_bar = n->next;
+				} else {
+					if (first)
+						status_bar = NULL;
+				}
 
-	(void) wrefresh(title_bar_window);
-	(void) werase(status_bar_window);
+				free(n);
+				found = B_TRUE;
+			}
+		}
 
-	for (i = 0; i < PT_BAR_NSLOTS; i++) {
-		if (strlen(g_status_bar_slots[i]) == 0)
-			continue;
-		(void) wattron(status_bar_window, A_REVERSE);
-		print(status_bar_window, y, x, "%s", g_status_bar_slots[i]);
-		(void) wattroff(status_bar_window, A_REVERSE);
-		x += strlen(g_status_bar_slots[i]) + 1;
+		/*
+		 * Found and removed at least one occurrance of msg, refresh
+		 * the bar and return.
+		 */
+		if (found) {
+			return;
+		} else {
+			/*
+			 * Inserting a new msg, walk to the end of the bar.
+			 */
+			for (n = status_bar; n->next != NULL; n = n->next)
+				;
+		}
 	}
-	(void) wnoutrefresh(status_bar_window);
+
+	if ((new = calloc(1, sizeof (sb_slot_t))) == NULL) {
+		pt_error("%s : failed to allocate a new slot\n", __FILE__);
+	} else {
+		new->msg = strdup(msg);
+
+		/*
+		 * Check if it's the first entry.
+		 */
+		if (status_bar == NULL) {
+			status_bar = new;
+			new->prev = NULL;
+		} else {
+			new->prev = n;
+			n->next = new;
+		}
+		new->next = NULL;
+	}
 }
 
 void
-show_cstates(void)
+pt_display_states(void)
 {
 	char		c[100];
 	int		i;
 	double		total_pstates = 0.0, avg, res;
 	uint64_t	p0_speed, p1_speed;
 
-	if (!PT_ON_DUMP) {
-		(void) werase(cstate_window);
-		(void) wattrset(cstate_window, COLOR_PAIR(PT_COLOR_DEFAULT));
-		(void) wbkgd(cstate_window, COLOR_PAIR(PT_COLOR_DEFAULT));
+	print(sw[SW_IDLE], 0, 0, "%s\tAvg\tResidency\n", g_msg_idle_state);
+
+	if (g_features & FEATURE_CSTATE) {
+		res =  (((double)g_cstate_info[0].total_time / g_total_c_time))
+		    * 100;
+		(void) sprintf(c, "C0 (cpu running)\t\t(%.1f%%)\n", (float)res);
+		print(sw[SW_IDLE], 1, 0, "%s", c);
+
+		for (i = 1; i <= g_max_cstate; i++) {
+			/*
+			 * In situations where the load is too intensive, the
+			 * system might not transition at all.
+			 */
+			if (g_cstate_info[i].events > 0)
+				avg = (((double)g_cstate_info[i].total_time/
+				    MICROSEC)/g_cstate_info[i].events);
+			else
+				avg = 0;
+
+			res = ((double)g_cstate_info[i].total_time/
+			    g_total_c_time) * 100;
+
+			(void) sprintf(c, "C%d\t\t\t%.1fms\t(%.1f%%)\n",
+			    i, (float)avg, (float)res);
+			print(sw[SW_IDLE], i + 1, 0, "%s", c);
+		}
 	}
 
-	print(cstate_window, 0, 0, "%s\tAvg\tresidency\n", g_msg_idle_state);
-	res =  (((double)g_cstate_info[0].total_time / g_total_c_time)) * 100;
-	(void) sprintf(c, "C0 (cpu running)\t\t(%.1f%%)\n", (float)res);
-	print(cstate_window, 1, 0, "%s", c);
+	if (!PT_ON_DUMP)
+		(void) wnoutrefresh(sw[SW_IDLE]);
 
-	for (i = 1; i <= g_max_cstate; i++) {
-		/*
-		 * In situations where the load is too intensive, the system
-		 * might not transition at all.
-		 */
-		if (g_cstate_info[i].events > 0)
-			avg = (((double)g_cstate_info[i].total_time/
-			    MICROSEC)/g_cstate_info[i].events);
-		else
-			avg = 0;
+	print(sw[SW_FREQ], 0, 0, "%s\n", g_msg_freq_state);
 
-		res = ((double)g_cstate_info[i].total_time/g_total_c_time)
-		    * 100;
-
-		(void) sprintf(c, "C%d\t\t\t%.1fms\t(%.1f%%)\n", i, (float)avg,
-		    (float)res);
-		print(cstate_window, i + 1, 0, "%s", c);
-	}
-
-	print(cstate_window, 0, 48, "%s\n", g_msg_freq_state);
-
-	if (g_npstates < 2) {
-		(void) sprintf(c, "%4lu Mhz\t%.1f%%",
-		    (long)g_pstate_info[0].speed, 100.0);
-		print(cstate_window, 1, 48, "%s\n", c);
-	} else {
+	if (g_features & FEATURE_PSTATE) {
 		for (i = 0; i < g_npstates; i++) {
-			total_pstates += (double)(g_pstate_info[i].total_time/
+			total_pstates +=
+			    (double)(g_pstate_info[i].total_time/
 			    g_ncpus_observed/MICROSEC);
 		}
 
@@ -281,9 +434,9 @@
 		for (i = 0;  i < g_npstates - 1; i++) {
 			(void) sprintf(c, "%4lu Mhz\t%.1f%%",
 			    (long)g_pstate_info[i].speed,
-			    100 * (g_pstate_info[i].total_time/g_ncpus_observed/
-			    MICROSEC/total_pstates));
-			print(cstate_window, i+1, 48, "%s\n", c);
+			    100 * (g_pstate_info[i].total_time/
+			    g_ncpus_observed/MICROSEC/total_pstates));
+			print(sw[SW_FREQ], i+1, 0, "%s\n", c);
 		}
 
 		/*
@@ -301,12 +454,14 @@
 				p0_speed = p1_speed + 1;
 			} else {
 				/*
-				 * If g_turbo_ratio > 1.0, that means turbo
-				 * mode works. So, P(0) = ratio * P(1);
+				 * If g_turbo_ratio > 1.0, that means
+				 * turbo mode works. So, P(0) = ratio *
+				 *  P(1);
 				 */
-				p0_speed = (uint64_t)(p1_speed * g_turbo_ratio);
+				p0_speed = (uint64_t)(p1_speed *
+				    g_turbo_ratio);
 				if (p0_speed < (p1_speed + 1))
-				p0_speed = p1_speed + 1;
+					p0_speed = p1_speed + 1;
 			}
 			/*
 			 * Reset the ratio for the next round
@@ -326,15 +481,21 @@
 			    100 * (g_pstate_info[i].total_time/
 			    g_ncpus_observed/MICROSEC/total_pstates));
 		}
-		print(cstate_window, i+1, 48, "%s\n", c);
+		print(sw[SW_FREQ], i+1, 0, "%s\n", c);
+	} else {
+		if (g_npstates == 1) {
+			(void) sprintf(c, "%4lu Mhz\t%.1f%%",
+			    (long)g_pstate_info[0].speed, 100.0);
+			print(sw[SW_FREQ], 1, 0, "%s\n", c);
+		}
 	}
 
 	if (!PT_ON_DUMP)
-		(void) wnoutrefresh(cstate_window);
+		(void) wnoutrefresh(sw[SW_FREQ]);
 }
 
 void
-show_acpi_power_line(uint32_t flag, double rate, double rem_cap, double cap,
+pt_display_acpi_power(uint32_t flag, double rate, double rem_cap, double cap,
     uint32_t state)
 {
 	char	buffer[1024];
@@ -342,7 +503,8 @@
 	(void) sprintf(buffer,  _("no ACPI power usage estimate available"));
 
 	if (!PT_ON_DUMP)
-		(void) werase(acpi_power_window);
+		(void) werase(sw[SW_POWER]);
+
 	if (flag) {
 		char *c;
 		(void) sprintf(buffer, "Power usage (ACPI estimate): %.3fW",
@@ -368,22 +530,23 @@
 		}
 
 	}
-	print(acpi_power_window, 0, 0, "%s\n", buffer);
+
+	print(sw[SW_POWER], 0, 0, "%s\n", buffer);
 	if (!PT_ON_DUMP)
-		(void) wnoutrefresh(acpi_power_window);
+		(void) wnoutrefresh(sw[SW_POWER]);
 }
 
 void
-show_wakeups(double interval)
+pt_display_wakeups(double interval)
 {
 	char		c[100];
 	int		i, event_sum = 0;
 	event_info_t	*event = g_event_info;
 
 	if (!PT_ON_DUMP) {
-		(void) werase(wakeup_window);
-		(void) wbkgd(wakeup_window, COLOR_PAIR(PT_COLOR_RED));
-		(void) wattron(wakeup_window, A_BOLD);
+		(void) werase(sw[SW_WAKEUPS]);
+		(void) wbkgd(sw[SW_WAKEUPS], COLOR_PAIR(PT_COLOR_RED));
+		(void) wattron(sw[SW_WAKEUPS], A_BOLD);
 	}
 
 	/*
@@ -404,14 +567,14 @@
 
 	(void) sprintf(c, "Wakeups-from-idle per second: %4.1f\tinterval: "
 	    "%.1fs", (double)(g_total_events/interval), interval);
-	print(wakeup_window, 0, 0, "%s\n", c);
+	print(sw[SW_WAKEUPS], 0, 0, "%s\n", c);
 
 	if (!PT_ON_DUMP)
-		(void) wnoutrefresh(wakeup_window);
+		(void) wnoutrefresh(sw[SW_WAKEUPS]);
 }
 
 void
-show_eventstats(double interval)
+pt_display_events(double interval)
 {
 	char		c[100];
 	int		i;
@@ -419,9 +582,9 @@
 	event_info_t	*event = g_event_info;
 
 	if (!PT_ON_DUMP) {
-		(void) werase(eventstat_window);
-		(void) wattrset(eventstat_window, COLOR_PAIR(PT_COLOR_DEFAULT));
-		(void) wbkgd(eventstat_window, COLOR_PAIR(PT_COLOR_DEFAULT));
+		(void) werase(sw[SW_EVENTS]);
+		(void) wbkgd(sw[SW_EVENTS], COLOR_PAIR(PT_COLOR_DEFAULT));
+		(void) wattron(sw[SW_EVENTS], COLOR_PAIR(PT_COLOR_DEFAULT));
 	}
 
 	/*
@@ -439,7 +602,7 @@
 	else
 		(void) sprintf(c, "Top causes for wakeups:\n");
 
-	print(eventstat_window, 0, 0, "%s", c);
+	print(sw[SW_EVENTS], 0, 0, "%s", c);
 
 	for (i = 0; i < g_top_events; i++, event++) {
 
@@ -451,27 +614,26 @@
 
 		(void) sprintf(c, "%4.1f%% (%5.1f)", 100 * events,
 		    (double)event->total_count/interval);
-		print(eventstat_window, i+1, 0, "%s", c);
-		print(eventstat_window, i+1, 16, "%20s :",
+		print(sw[SW_EVENTS], i+1, 0, "%s", c);
+		print(sw[SW_EVENTS], i+1, 16, "%20s :",
 		    event->offender_name);
-		print(eventstat_window, i+1, 40, "%-64s\n",
+		print(sw[SW_EVENTS], i+1, 40, "%-64s\n",
 		    event->offense_name);
 	}
 
 	if (!PT_ON_DUMP)
-		(void) wnoutrefresh(eventstat_window);
+		(void) wnoutrefresh(sw[SW_EVENTS]);
 }
 
 void
-show_suggestion(char *sug)
+pt_display_suggestions(char *sug)
 {
-	(void) werase(suggestion_window);
-	print(suggestion_window, 0, 0, "%s", sug);
-	(void) wnoutrefresh(suggestion_window);
+	(void) werase(sw[SW_SUGG]);
+
+	if (sug != NULL)
+		print(sw[SW_SUGG], 0, 0, "%s", sug);
+
+	(void) wnoutrefresh(sw[SW_SUGG]);
+
+	pt_display_update();
 }
-
-void
-update_windows(void)
-{
-	(void) doupdate();
-}
--- a/usr/src/cmd/powertop/common/powertop.c	Fri Jun 19 20:12:07 2009 +0800
+++ b/usr/src/cmd/powertop/common/powertop.c	Fri Jun 19 06:14:38 2009 -0700
@@ -41,6 +41,7 @@
 #include <unistd.h>
 #include <stdio.h>
 #include <stdlib.h>
+#include <signal.h>
 #include <string.h>
 #include <ctype.h>
 #include <locale.h>
@@ -52,11 +53,11 @@
  */
 double 			g_ticktime, g_ticktime_usr;
 double 			g_interval;
-double			g_displaytime;
 
 int			g_bit_depth;
 int 			g_total_events, g_top_events;
 int			g_npstates, g_max_cstate, g_longest_cstate;
+uint_t			g_features;
 uint_t			g_ncpus;
 uint_t			g_ncpus_observed;
 
@@ -72,7 +73,6 @@
 state_info_t		g_cstate_info[NSTATES];
 freq_state_info_t	g_pstate_info[NSTATES];
 cpu_power_info_t	*g_cpu_power_states;
-suggestion_func 	*g_suggestion_activate;
 
 boolean_t		g_turbo_supported;
 
@@ -87,10 +87,11 @@
 main(int argc, char **argv)
 {
 	hrtime_t 	last, now;
-	uint_t		features = 0, user_interval = 0;
-	int		ncursesinited = 0, index2 = 0, c, ret, dump_count = 0;
+	uint_t		user_interval = 0;
+	int		index2 = 0, c, ret, dump_count = 0;
 	double		last_time;
 	char		*endptr;
+	boolean_t	root_user = B_FALSE;
 
 	static struct option opts[] = {
 		{ "dump", 1, NULL, 'd' },
@@ -108,8 +109,7 @@
 	pt_set_progname(argv[0]);
 
 	/*
-	 * Enumerate the system's CPUs
-	 * Populate cpu_table, g_ncpus
+	 * Enumerate the system's CPUs, populate cpu_table, g_ncpus
 	 */
 	if ((g_ncpus = g_ncpus_observed = enumerate_cpus()) == 0)
 		exit(EXIT_FAILURE);
@@ -117,15 +117,16 @@
 	if ((g_bit_depth = get_bit_depth()) < 0)
 		exit(EXIT_FAILURE);
 
+	g_features = 0;
 	g_ticktime = g_ticktime_usr = INTERVAL_DEFAULT;
-	g_displaytime 	= 0.0;
-	g_op_mode	= PT_MODE_DEFAULT;
-	g_gui		= B_FALSE;
-	g_max_cstate	= 0;
-	g_argv		= NULL;
-	g_argc		= 0;
-	g_observed_cpu	= 0;
+	g_op_mode = PT_MODE_DEFAULT;
+	g_gui = B_FALSE;
+	g_max_cstate = 0;
+	g_argv = NULL;
+	g_argc = 0;
+	g_observed_cpu = 0;
 	g_turbo_supported = B_FALSE;
+	g_curr_sugg = NULL;
 
 	while ((c = getopt_long(argc, argv, "d:t:h:vc:", opts, &index2))
 	    != EOF) {
@@ -189,22 +190,22 @@
 		}
 	}
 
-	if (optind < argc) {
+	if (optind < argc)
 		usage();
-	}
 
 	(void) printf("%s   %s\n\n", TITLE, COPYRIGHT_INTEL);
 
-	/*
-	 * If the system is running on battery, find out what's
-	 * the kstat module for it
-	 */
-	battery_mod_lookup();
+	(void) printf(_("Collecting data for %.2f second(s) \n"),
+	    (float)g_ticktime);
+
+	/* Prepare P-state statistics */
+	if (pt_cpufreq_stat_prepare() == 0)
+		g_features |= FEATURE_PSTATE;
 
 	/* Prepare C-state statistics */
 	ret = pt_cpuidle_stat_prepare();
 	if (ret == 0)
-		features |= FEATURE_CSTATE;
+		g_features |= FEATURE_CSTATE;
 	else
 		/*
 		 * PowerTop was unable to run a DTrace program,
@@ -212,31 +213,51 @@
 		 */
 		exit(EXIT_FAILURE);
 
-	/* Prepare P-state statistics */
-	if (pt_cpufreq_stat_prepare() == 0)
-		features |= FEATURE_PSTATE;
+	/*
+	 * We need to initiate the display to make sure there's enough space
+	 * in the terminal for all of PowerTOP's subwindows, but after
+	 * pt_cpufreq_stat_prepare() which finds out how many states the
+	 * system supports.
+	 */
+	if (!PT_ON_DUMP) {
+		pt_display_init_curses();
+		pt_display_setup(B_FALSE);
+		g_gui = B_TRUE;
+		pt_display_title_bar();
+		pt_display_status_bar();
+	}
 
 	/* Prepare event statistics */
 	if (pt_events_stat_prepare() != -1)
-		features |= FEATURE_EVENTS;
+		g_features |= FEATURE_EVENTS;
+
+	/*
+	 * If the system is running on battery, find out what's
+	 * the kstat module for it
+	 */
+	battery_mod_lookup();
 
 	/* Prepare turbo statistics */
-	if (pt_turbo_stat_prepare() == 0) {
-		features |= FEATURE_TURBO;
-	}
+	if (pt_turbo_stat_prepare() == 0)
+		g_features |= FEATURE_TURBO;
 
-	(void) printf(_("Collecting data for %.2f second(s) \n"),
-	    (float)g_ticktime);
-
-	if (!PT_ON_DUMP)
-		g_gui = B_TRUE;
+	/*
+	 * Installs the initial suggestions, running as root and turning CPU
+	 * power management ON.
+	 */
+	if (geteuid() != 0)
+		pt_sugg_as_root();
+	else {
+		root_user = B_TRUE;
+		pt_cpufreq_suggest();
+	}
 
 	last = gethrtime();
 
 	while (true) {
 		fd_set 	rfds;
 		struct 	timeval tv;
-		int 	key, reinit = 0;
+		int 	key;
 		char 	keychar;
 
 		/*
@@ -246,18 +267,18 @@
 		FD_ZERO(&rfds);
 		FD_SET(0, &rfds);
 
-		tv.tv_sec 	= (long)g_ticktime;
-		tv.tv_usec 	= (long)((g_ticktime - tv.tv_sec) * MICROSEC);
+		tv.tv_sec = (long)g_ticktime;
+		tv.tv_usec = (long)((g_ticktime - tv.tv_sec) * MICROSEC);
 
-		if (!PT_ON_DUMP)
+		if (!PT_ON_DUMP) {
 			key = select(1, &rfds, NULL, NULL, &tv);
-		else
+		} else
 			key = select(1, NULL, NULL, NULL, &tv);
 
-		now 		= gethrtime();
+		now = gethrtime();
 
-		g_interval 	= (double)(now - last)/NANOSEC;
-		last 		= now;
+		g_interval = (double)(now - last)/NANOSEC;
+		last = now;
 
 		g_top_events 	= 0;
 		g_total_events 	= 0;
@@ -268,145 +289,114 @@
 		    NSTATES * sizeof (state_info_t));
 
 		/* Collect idle state transition stats */
-		if (features & FEATURE_CSTATE &&
+		if (g_features & FEATURE_CSTATE &&
 		    pt_cpuidle_stat_collect(g_interval) < 0) {
 			/* Reinitialize C-state statistics */
 			if (pt_cpuidle_stat_prepare() != 0)
 				exit(EXIT_FAILURE);
 
-			reinit = 1;
+			continue;
 		}
 
 		/* Collect frequency change stats */
-		if (features & FEATURE_PSTATE &&
+		if (g_features & FEATURE_PSTATE &&
 		    pt_cpufreq_stat_collect(g_interval) < 0) {
 			/* Reinitialize P-state statistics */
 			if (pt_cpufreq_stat_prepare() != 0)
 				exit(EXIT_FAILURE);
 
-			reinit = 1;
+			continue;
 		}
 
 		/* Collect event statistics */
-		if (features & FEATURE_EVENTS &&
+		if (g_features & FEATURE_EVENTS &&
 		    pt_events_stat_collect() < 0) {
 			/* Reinitialize event statistics */
 			if (pt_events_stat_prepare() != 0)
 				exit(EXIT_FAILURE);
 
-			reinit = 1;
-		}
-
-		if (reinit)
 			continue;
-
-		/* Collect turbo statistics */
-		if (features & FEATURE_TURBO &&
-		    pt_turbo_stat_collect() < 0) {
-			exit(EXIT_FAILURE);
 		}
 
-		/*
-		 * Initialize curses if we're not dumping and
-		 * haven't already done it
-		 */
-		if (!PT_ON_DUMP) {
-			if (!ncursesinited) {
-				initialize_curses();
-				ncursesinited++;
-			}
-			setup_windows();
-			show_title_bar();
+		/* Collect turbo statistics */
+		if (g_features & FEATURE_TURBO &&
+		    pt_turbo_stat_collect() < 0)
+			exit(EXIT_FAILURE);
+
+		/* Show CPU power states */
+		pt_display_states();
+
+		/* Show wakeups events affecting PM */
+		if (g_features & FEATURE_EVENTS) {
+			pt_display_wakeups(g_interval);
+			pt_display_events(g_interval);
 		}
 
-		/* Show CPU power states */
-		if (features & FEATURE_CSTATE)
-			show_cstates();
-
-		/* Show wakeups events affecting PM */
-		if (features & FEATURE_EVENTS) {
-			show_wakeups(g_interval);
-			show_eventstats(g_interval);
-		}
-
-		print_battery();
-
-		g_displaytime = g_displaytime - g_ticktime;
+		pt_battery_print();
 
 		if (key && !PT_ON_DUMP) {
 			keychar = toupper(fgetc(stdin));
 
 			switch (keychar) {
 			case 'Q':
-				cleanup_curses();
 				exit(EXIT_SUCCESS);
 				break;
+
 			case 'R':
 				g_ticktime = 3;
 				break;
 			}
-			if (keychar == g_suggestion_key &&
-			    g_suggestion_activate) {
-				g_suggestion_activate();
-				g_displaytime = -1.0;
-			}
-		}
-		reset_suggestions();
 
-		/* suggests PM */
-		if (geteuid() == 0) {
-			suggest_p_state();
-		} else {
-			suggest_as_root();
+			/*
+			 * Check if the user has activated the current
+			 * suggestion.
+			 */
+			if (g_curr_sugg != NULL &&
+			    keychar == g_curr_sugg->key && g_curr_sugg->func)
+				g_curr_sugg->func();
 		}
 
 		if (dump_count)
 			dump_count--;
 
 		/* Exits if user requested a dump */
-		if (PT_ON_DUMP && !dump_count) {
-			print_all_suggestions();
+		if (PT_ON_DUMP && !dump_count)
 			exit(EXIT_SUCCESS);
-		}
 
 		/* No key pressed, will suggest something */
 		if (!key && !dump_count)
-			pick_suggestion();
+			pt_sugg_pick();
 
 		/* Refresh display */
-		if (!PT_ON_DUMP) {
-			show_title_bar();
-			update_windows();
-		}
+		if (!PT_ON_DUMP)
+			pt_display_update();
+
+		if (root_user)
+			pt_cpufreq_suggest();
 
 		/*
 		 * Update the interval based on how long the CPU was in the
 		 * longest c-state during the last snapshot. If the user
 		 * specified an interval we skip this bit and keep it fixed.
 		 */
-		last_time = (((double)g_cstate_info[g_longest_cstate].total_time
-		    /MICROSEC/g_ncpus)/g_cstate_info[g_longest_cstate].events);
+		if (g_features & FEATURE_CSTATE && !user_interval) {
+			last_time = (((double)
+			    g_cstate_info[g_longest_cstate].total_time/MICROSEC
+			    /g_ncpus)/g_cstate_info[g_longest_cstate].events);
 
-		if (!user_interval)
 			if (last_time < INTERVAL_DEFAULT ||
 			    (g_total_events/g_ticktime) < 1)
 				g_ticktime = INTERVAL_DEFAULT;
 			else
 				g_ticktime = INTERVAL_UPDATE(last_time);
+		} else {
+			/*
+			 * Restore interval after a refresh.
+			 */
+			if (key)
+				g_ticktime = g_ticktime_usr;
+		}
+	}
 
-		/*
-		 * Restore user specified interval after a refresh
-		 */
-		if (keychar == 'R' && user_interval)
-			g_ticktime = g_ticktime_usr;
-	}
 	return (EXIT_SUCCESS);
 }
-
-void
-suggest_as_root(void)
-{
-	add_suggestion("Suggestion: run as root to get suggestions"
-	    " for reducing system power consumption",  40, NULL, NULL,
-	    NULL);
-}
--- a/usr/src/cmd/powertop/common/powertop.h	Fri Jun 19 20:12:07 2009 +0800
+++ b/usr/src/cmd/powertop/common/powertop.h	Fri Jun 19 06:14:38 2009 -0700
@@ -48,7 +48,7 @@
 
 #define	_(STRING)		gettext(STRING)
 
-#define	TITLE			"OpenSolaris PowerTOP version 1.1"
+#define	TITLE			"OpenSolaris PowerTOP version 1.2"
 #define	COPYRIGHT_INTEL		"(C) 2009 Intel Corporation"
 
 /*
@@ -79,29 +79,6 @@
 #define	NSTATES			32
 
 /*
- * Display colors
- */
-#define	PT_COLOR_DEFAULT	1
-#define	PT_COLOR_HEADER_BAR	2
-#define	PT_COLOR_ERROR		3
-#define	PT_COLOR_RED		4
-#define	PT_COLOR_YELLOW		5
-#define	PT_COLOR_GREEN		6
-#define	PT_COLOR_BRIGHT		7
-#define	PT_COLOR_BLUE		8
-
-/*
- * Constants for setup_windows()
- */
-#define	SINGLE_LINE_SW 		1
-#define	LENGTH_SUGG_SW		2
-#define	TITLE_LINE		1
-#define	BLANK_LINE		1
-#define	NEXT_LINE		1
-#define	PT_BAR_NSLOTS		10
-#define	PT_BAR_LENGTH		40
-
-/*
  * Available op modes
  */
 #define	PT_MODE_DEFAULT		0x01
@@ -158,12 +135,21 @@
 	uint64_t	t_acnt;
 } turbo_info_t;
 
-typedef	void			(suggestion_func)(void);
+/*
+ * Suggestions
+ */
+typedef	void		(sugg_func_t)(void);
 
-/*
- * Global variables
- */
-extern double			g_displaytime;
+typedef struct suggestion {
+	char *text;
+	char key;
+	char *sb_msg;
+	int weight;
+	int slice;
+	sugg_func_t *func;
+	struct suggestion *prev;
+	struct suggestion *next;
+} sugg_t;
 
 extern int			g_bit_depth;
 
@@ -213,12 +199,10 @@
 extern state_info_t		g_cstate_info[NSTATES];
 extern freq_state_info_t	g_pstate_info[NSTATES];
 
+extern uint_t			g_features;
 extern uint_t			g_ncpus;
 extern uint_t			g_ncpus_observed;
 
-extern char 			g_status_bar_slots[PT_BAR_NSLOTS]
-	[PT_BAR_LENGTH];
-
 extern cpu_power_info_t		*g_cpu_power_states;
 
 /*
@@ -227,8 +211,7 @@
 extern boolean_t		g_turbo_supported;
 extern double			g_turbo_ratio;
 
-extern char 			g_suggestion_key;
-extern suggestion_func 		*g_suggestion_activate;
+extern sugg_t			*g_curr_sugg;
 
 /*
  * DTrace scripts for the events report
@@ -247,13 +230,15 @@
 /*
  * Platform specific messages
  */
-extern const char 		*g_msg_idle_state;
-extern const char 		*g_msg_freq_state;
+extern const char		*g_msg_idle_state;
+extern const char		*g_msg_freq_state;
+extern const char		*g_msg_freq_enable;
+
 /*
  * Suggestions related
  */
-extern void 		suggest_p_state(void);
-extern void		suggest_as_root(void);
+extern void 		pt_cpufreq_suggest(void);
+extern void		pt_sugg_as_root(void);
 
 /*
  * See util.c
@@ -269,27 +254,30 @@
 /*
  * Display/curses related
  */
-extern void 		show_title_bar(void);
-extern void 		setup_windows(void);
-extern void 		initialize_curses(void);
-extern void		show_acpi_power_line(uint32_t, double, double, double,
+extern void		pt_display_setup(boolean_t);
+extern void 		pt_display_init_curses(void);
+extern void		pt_display_update(void);
+extern void 		pt_display_title_bar(void);
+extern void		pt_display_status_bar(void);
+extern void		pt_display_mod_status_bar(char *);
+extern void 		pt_display_states(void);
+extern void		pt_display_acpi_power(uint32_t, double, double, double,
 	uint32_t);
-extern void 		show_cstates();
-extern void 		show_wakeups(double);
-extern void 		show_eventstats(double);
-extern void 		show_suggestion(char *);
-extern void 		cleanup_curses(void);
-extern void		update_windows(void);
+extern void 		pt_display_wakeups(double);
+extern void 		pt_display_events(double);
+extern void 		pt_display_suggestions(char *);
 
 /*
  * Suggestions
  */
-extern void 		pick_suggestion(void);
-extern void 		add_suggestion(char *, int, char, char *,
-	suggestion_func *);
-extern void 		reset_suggestions(void);
-extern void 		print_all_suggestions(void);
-extern void 		print_battery(void);
+extern void 		pt_sugg_add(char *, int, char, char *, sugg_func_t *);
+extern int		pt_sugg_remove(sugg_func_t *);
+extern void 		pt_sugg_pick(void);
+
+/*
+ * Battery
+ */
+extern void 		pt_battery_print(void);
 
 /*
  * DTrace stats
--- a/usr/src/cmd/powertop/common/suggestions.c	Fri Jun 19 20:12:07 2009 +0800
+++ b/usr/src/cmd/powertop/common/suggestions.c	Fri Jun 19 06:14:38 2009 -0700
@@ -43,136 +43,231 @@
 #include <string.h>
 #include "powertop.h"
 
-struct suggestion;
+/*
+ * Default number of intervals we display a suggestion before moving
+ * to the next.
+ */
+#define	PT_SUGG_DEF_SLICE	3
+
+/*
+ * Global pointer to the current suggestion.
+ */
+sugg_t	*g_curr_sugg;
+
+/*
+ * Head of the list of suggestions.
+ */
+static sugg_t *sugg;
 
-struct suggestion {
-	struct suggestion *next;
+/*
+ * Add a new suggestion. Only one suggestion per text allowed.
+ */
+void
+pt_sugg_add(char *text, int weight, char key, char *sb_msg, sugg_func_t *func)
+{
+	sugg_t *new, *n, *pos = NULL;
+
+	/*
+	 * Text is a required field for suggestions
+	 */
+	if (text == NULL)
+		return;
+
+	if (sugg == NULL) {
+		/*
+		 * Creating first element
+		 */
+		if ((new = calloc(1, sizeof (sugg_t))) == NULL)
+			return;
 
-	char 	*string;
-	int	weight;
-	char 	key;
-	char 	*keystring;
+		if (sb_msg != NULL)
+			new->sb_msg = strdup(sb_msg);
+
+		if (text != NULL)
+			new->text = strdup(text);
+
+		new->weight = weight;
+		new->key = key;
+		new->func = func;
+		new->slice = 0;
+
+		sugg = new;
+		new->prev = NULL;
+		new->next = NULL;
+	} else {
+		for (n = sugg; n != NULL; n = n->next) {
+			if (strcmp(n->text, text) == 0)
+				return;
 
-	suggestion_func *func;
-};
+			if (weight > n->weight && pos == NULL)
+				pos = n;
+		}
+		/*
+		 * Create a new element
+		 */
+		if ((new = calloc(1, sizeof (sugg_t))) == NULL)
+			return;
 
-static struct suggestion 	*suggestions;
-static int 			total_weight;
+		if (sb_msg != NULL)
+			new->sb_msg = strdup(sb_msg);
+
+		new->text = strdup(text);
+
+		new->weight = weight;
+		new->key = key;
+		new->func = func;
+		new->slice = 0;
 
-static char 	previous[1024];
+		if (pos == NULL) {
+			/*
+			 * Ordering placed the new element at the end
+			 */
+			for (n = sugg; n->next != NULL; n = n->next)
+				;
 
-void
-reset_suggestions(void)
-{
-	struct suggestion *ptr;
-
-	ptr = suggestions;
+			n->next = new;
+			new->prev = n;
+			new->next = NULL;
+		} else {
+			if (pos == sugg) {
+				/*
+				 * Ordering placed the new element at the start
+				 */
+				new->next = sugg;
+				new->prev = sugg;
+				sugg->prev = new;
+				sugg = new;
+			} else {
+				/*
+				 * Ordering placed the new element somewhere in
+				 * the middle
+				 */
+				new->next = pos;
+				new->prev = pos->prev;
+				pos->prev->next = new;
+				pos->prev = new;
+			}
+		}
+	}
+}
 
-	while (ptr) {
-		struct suggestion *next;
+/*
+ * Removes a suggestion, returning 0 if not found and 1 if so.
+ */
+int
+pt_sugg_remove(sugg_func_t *func)
+{
+	sugg_t *n;
+	int ret = 0;
 
-		next = ptr->next;
-		free(ptr->string);
-		free(ptr->keystring);
-		free(ptr);
-		ptr = next;
+	for (n = sugg; n != NULL; n = n->next) {
+		if (n->func == func) {
+			/* Removing the first element */
+			if (n == sugg) {
+				if (sugg->next == NULL) {
+					/* Removing the only element */
+					sugg = NULL;
+				} else {
+					sugg = n->next;
+					sugg->prev = NULL;
+				}
+			} else {
+				if (n->next == NULL) {
+					/* Removing the last element */
+					n->prev->next = NULL;
+				} else {
+					/* Removing an intermediate element */
+					n->prev->next = n->next;
+					n->next->prev = n->prev;
+				}
+			}
+
+			/*
+			 * If this suggestions is currently being suggested,
+			 * remove it and update the screen.
+			 */
+			if (n == g_curr_sugg) {
+				if (n->sb_msg != NULL) {
+					pt_display_mod_status_bar(n->sb_msg);
+					pt_display_status_bar();
+				}
+				if (n->text != NULL)
+					pt_display_suggestions(NULL);
+			}
+
+			free(n);
+			ret = 1;
+		}
 	}
 
-	suggestions = NULL;
-	(void) strcpy(g_status_bar_slots[8], "");
+	return (ret);
+}
+
+/*
+ * Chose a suggestion to display. The list of suggestions is ordered by weight,
+ * so we only worry about fariness here. Each suggestion, starting with the
+ * first (the 'heaviest') is displayed during PT_SUGG_DEF_SLICE intervals.
+ */
+void
+pt_sugg_pick(void)
+{
+	sugg_t *n;
+
+	if (sugg == NULL) {
+		g_curr_sugg = NULL;
+		return;
+	}
+
+search:
+	for (n = sugg; n != NULL; n = n->next) {
+
+		if (n->slice++ < PT_SUGG_DEF_SLICE) {
+
+			/*
+			 * Don't need to re-suggest the current suggestion.
+			 */
+			if (g_curr_sugg == n)
+				return;
 
-	g_suggestion_key 	= -1;
-	g_suggestion_activate 	= NULL;
-	total_weight 		= 0;
+			/*
+			 * Remove the current suggestion from screen.
+			 */
+			if (g_curr_sugg != NULL) {
+				if (g_curr_sugg->sb_msg != NULL) {
+					pt_display_mod_status_bar(
+					    g_curr_sugg->sb_msg);
+					pt_display_status_bar();
+				}
+				if (g_curr_sugg->text != NULL)
+					pt_display_suggestions(NULL);
+			}
+
+			if (n->sb_msg != NULL) {
+				pt_display_mod_status_bar(n->sb_msg);
+				pt_display_status_bar();
+			}
+
+			pt_display_suggestions(n->text);
+
+			g_curr_sugg = n;
+
+			return;
+		}
+	}
+
+	/*
+	 * All suggestions have run out of slice quotas, so we restart.
+	 */
+	for (n = sugg; n != NULL; n = n->next)
+		n->slice = 0;
+
+	goto search;
 }
 
 void
-add_suggestion(char *text, int weight, char key, char *keystring,
-    suggestion_func *func)
-{
-	struct suggestion *new;
-
-	if (!text)
-		return;
-
-	new = malloc(sizeof (struct suggestion));
-
-	if (!new)
-		return;
-
-	(void) memset(new, 0, sizeof (struct suggestion));
-
-	new->string = strdup(text);
-	new->weight = weight;
-	new->key = key;
-
-	if (keystring)
-		new->keystring = strdup(keystring);
-
-	new->next 	= suggestions;
-	new->func 	= func;
-	suggestions 	= new;
-	total_weight 	+= weight;
-}
-
-void
-pick_suggestion(void)
+pt_sugg_as_root(void)
 {
-	int			weight, value, running = 0;
-	struct suggestion 	*ptr;
-
-	(void) strcpy(g_status_bar_slots[8], "");
-	g_suggestion_key 	= -1;
-	g_suggestion_activate 	= NULL;
-
-	if (total_weight == 0 || suggestions == NULL) {
-		show_suggestion("");
-		return;
-	}
-
-	weight = total_weight;
-
-	if (strlen(previous) && g_displaytime > 0.0)
-		weight += 50;
-
-	value 	= rand() % weight;
-	ptr 	= suggestions;
-
-	while (ptr) {
-		running += ptr->weight;
-
-		if (strcmp(ptr->string, previous) == 0 && g_displaytime > 0.0)
-			running += 50;
-
-		if (running > value) {
-			if (ptr->keystring)
-				(void) strncpy(g_status_bar_slots[8],
-				    ptr->keystring, PT_BAR_LENGTH);
-
-			g_suggestion_key 	= ptr->key;
-			g_suggestion_activate 	= ptr->func;
-
-			show_suggestion(ptr->string);
-
-			if (strcmp(ptr->string, previous)) {
-				g_displaytime = 30.0;
-				(void) strcpy(previous, ptr->string);
-			}
-			return;
-		}
-		ptr = ptr->next;
-	}
-
-	show_suggestion("");
-	(void) memset(previous, 0, sizeof (previous));
-	g_displaytime = -1.0;
+	pt_sugg_add("Suggestion: run as root to get suggestions"
+	    " for reducing system power consumption",  40, NULL, NULL,
+	    NULL);
 }
-
-void
-print_all_suggestions(void)
-{
-	struct suggestion *ptr;
-
-	for (ptr = suggestions; ptr; ptr = ptr->next)
-		(void) printf("\n%s\n", ptr->string);
-}
--- a/usr/src/cmd/powertop/i386/pt_i386.c	Fri Jun 19 20:12:07 2009 +0800
+++ b/usr/src/cmd/powertop/i386/pt_i386.c	Fri Jun 19 06:14:38 2009 -0700
@@ -217,3 +217,4 @@
  */
 const char *g_msg_idle_state = "C-states (idle power)";
 const char *g_msg_freq_state = "P-states (frequencies)";
+const char *g_msg_freq_enable = "P - Enable P-states";
--- a/usr/src/cmd/powertop/sparcv9/pt_sparcv9.c	Fri Jun 19 20:12:07 2009 +0800
+++ b/usr/src/cmd/powertop/sparcv9/pt_sparcv9.c	Fri Jun 19 06:14:38 2009 -0700
@@ -245,5 +245,6 @@
 /*
  * sparcv9 platform specific display messages
  */
-const char *g_msg_idle_state = "Idle Power States\t";
+const char *g_msg_idle_state = "Idle Power States";
 const char *g_msg_freq_state = "Frequency Levels";
+const char *g_msg_freq_enable = "P - Enable CPU PM";