1 /*
   2  * Copyright 2009, Intel Corporation
   3  * Copyright 2009, Sun Microsystems, Inc
   4  *
   5  * This file is part of PowerTOP
   6  *
   7  * This program file is free software; you can redistribute it and/or modify it
   8  * under the terms of the GNU General Public License as published by the
   9  * Free Software Foundation; version 2 of the License.
  10  *
  11  * This program is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * for more details.
  15  *
  16  * You should have received a copy of the GNU General Public License
  17  * along with this program in a file named COPYING; if not, write to the
  18  * Free Software Foundation, Inc.,
  19  * 51 Franklin Street, Fifth Floor,
  20  * Boston, MA 02110-1301 USA
  21  *
  22  * Authors:
  23  *      Arjan van de Ven <arjan@linux.intel.com>
  24  *      Eric C Saxe <eric.saxe@sun.com>
  25  *      Aubrey Li <aubrey.li@intel.com>
  26  */
  27 
  28 /*
  29  * GPL Disclaimer
  30  *
  31  * For the avoidance of doubt, except that if any license choice other
  32  * than GPL or LGPL is available it will apply instead, Sun elects to
  33  * use only the General Public License version 2 (GPLv2) at this time
  34  * for any software where a choice of GPL license versions is made
  35  * available with the language indicating that GPLv2 or any later
  36  * version may be used, or where a choice of which version of the GPL
  37  * is applied is otherwise unspecified.
  38  */
  39 
  40 #include <unistd.h>
  41 #include <stdio.h>
  42 #include <stdlib.h>
  43 #include <string.h>
  44 #include "powertop.h"
  45 
  46 /*
  47  * Default number of intervals we display a suggestion before moving
  48  * to the next.
  49  */
  50 #define PT_SUGG_DEF_SLICE       3
  51 
  52 /*
  53  * Global pointer to the current suggestion.
  54  */
  55 sugg_t  *g_curr_sugg;
  56 
  57 /*
  58  * Head of the list of suggestions.
  59  */
  60 static sugg_t *sugg;
  61 
  62 /*
  63  * Add a new suggestion. Only one suggestion per text allowed.
  64  */
  65 void
  66 pt_sugg_add(char *text, int weight, char key, char *sb_msg, sugg_func_t *func)
  67 {
  68         sugg_t *new, *n, *pos = NULL;
  69 
  70         /*
  71          * Text is a required field for suggestions
  72          */
  73         if (text == NULL)
  74                 return;
  75 
  76         if (sugg == NULL) {
  77                 /*
  78                  * Creating first element
  79                  */
  80                 if ((new = calloc(1, sizeof (sugg_t))) == NULL)
  81                         return;
  82 
  83                 if (sb_msg != NULL)
  84                         new->sb_msg = strdup(sb_msg);
  85 
  86                 if (text != NULL)
  87                         new->text = strdup(text);
  88 
  89                 new->weight = weight;
  90                 new->key = key;
  91                 new->func = func;
  92                 new->slice = 0;
  93 
  94                 sugg = new;
  95                 new->prev = NULL;
  96                 new->next = NULL;
  97         } else {
  98                 for (n = sugg; n != NULL; n = n->next) {
  99                         if (strcmp(n->text, text) == 0)
 100                                 return;
 101 
 102                         if (weight > n->weight && pos == NULL)
 103                                 pos = n;
 104                 }
 105                 /*
 106                  * Create a new element
 107                  */
 108                 if ((new = calloc(1, sizeof (sugg_t))) == NULL)
 109                         return;
 110 
 111                 if (sb_msg != NULL)
 112                         new->sb_msg = strdup(sb_msg);
 113 
 114                 new->text = strdup(text);
 115 
 116                 new->weight = weight;
 117                 new->key = key;
 118                 new->func = func;
 119                 new->slice = 0;
 120 
 121                 if (pos == NULL) {
 122                         /*
 123                          * Ordering placed the new element at the end
 124                          */
 125                         for (n = sugg; n->next != NULL; n = n->next)
 126                                 ;
 127 
 128                         n->next = new;
 129                         new->prev = n;
 130                         new->next = NULL;
 131                 } else {
 132                         if (pos == sugg) {
 133                                 /*
 134                                  * Ordering placed the new element at the start
 135                                  */
 136                                 new->next = sugg;
 137                                 new->prev = sugg;
 138                                 sugg->prev = new;
 139                                 sugg = new;
 140                         } else {
 141                                 /*
 142                                  * Ordering placed the new element somewhere in
 143                                  * the middle
 144                                  */
 145                                 new->next = pos;
 146                                 new->prev = pos->prev;
 147                                 pos->prev->next = new;
 148                                 pos->prev = new;
 149                         }
 150                 }
 151         }
 152 }
 153 
 154 /*
 155  * Removes a suggestion, returning 0 if not found and 1 if so.
 156  */
 157 int
 158 pt_sugg_remove(sugg_func_t *func)
 159 {
 160         sugg_t *n;
 161         int ret = 0;
 162 
 163         for (n = sugg; n != NULL; n = n->next) {
 164                 if (n->func == func) {
 165                         /* Removing the first element */
 166                         if (n == sugg) {
 167                                 if (sugg->next == NULL) {
 168                                         /* Removing the only element */
 169                                         sugg = NULL;
 170                                 } else {
 171                                         sugg = n->next;
 172                                         sugg->prev = NULL;
 173                                 }
 174                         } else {
 175                                 if (n->next == NULL) {
 176                                         /* Removing the last element */
 177                                         n->prev->next = NULL;
 178                                 } else {
 179                                         /* Removing an intermediate element */
 180                                         n->prev->next = n->next;
 181                                         n->next->prev = n->prev;
 182                                 }
 183                         }
 184 
 185                         /*
 186                          * If this suggestions is currently being suggested,
 187                          * remove it and update the screen.
 188                          */
 189                         if (n == g_curr_sugg) {
 190                                 if (n->sb_msg != NULL) {
 191                                         pt_display_mod_status_bar(n->sb_msg);
 192                                         pt_display_status_bar();
 193                                 }
 194                                 if (n->text != NULL)
 195                                         pt_display_suggestions(NULL);
 196                         }
 197 
 198                         free(n);
 199                         ret = 1;
 200                 }
 201         }
 202 
 203         return (ret);
 204 }
 205 
 206 /*
 207  * Chose a suggestion to display. The list of suggestions is ordered by weight,
 208  * so we only worry about fariness here. Each suggestion, starting with the
 209  * first (the 'heaviest') is displayed during PT_SUGG_DEF_SLICE intervals.
 210  */
 211 void
 212 pt_sugg_pick(void)
 213 {
 214         sugg_t *n;
 215 
 216         if (sugg == NULL) {
 217                 g_curr_sugg = NULL;
 218                 return;
 219         }
 220 
 221 search:
 222         for (n = sugg; n != NULL; n = n->next) {
 223 
 224                 if (n->slice++ < PT_SUGG_DEF_SLICE) {
 225 
 226                         /*
 227                          * Don't need to re-suggest the current suggestion.
 228                          */
 229                         if (g_curr_sugg == n && !g_sig_resize)
 230                                 return;
 231 
 232                         /*
 233                          * Remove the current suggestion from screen.
 234                          */
 235                         if (g_curr_sugg != NULL) {
 236                                 if (g_curr_sugg->sb_msg != NULL) {
 237                                         pt_display_mod_status_bar(
 238                                             g_curr_sugg->sb_msg);
 239                                         pt_display_status_bar();
 240                                 }
 241                                 if (g_curr_sugg->text != NULL)
 242                                         pt_display_suggestions(NULL);
 243                         }
 244 
 245                         if (n->sb_msg != NULL) {
 246                                 pt_display_mod_status_bar(n->sb_msg);
 247                                 pt_display_status_bar();
 248                         }
 249 
 250                         pt_display_suggestions(n->text);
 251 
 252                         g_curr_sugg = n;
 253 
 254                         return;
 255                 }
 256         }
 257 
 258         /*
 259          * All suggestions have run out of slice quotas, so we restart.
 260          */
 261         for (n = sugg; n != NULL; n = n->next)
 262                 n->slice = 0;
 263 
 264         goto search;
 265 }
 266 
 267 void
 268 pt_sugg_as_root(void)
 269 {
 270         pt_sugg_add("Suggestion: run as root to get suggestions"
 271             " for reducing system power consumption",  40, '\0', NULL,
 272             NULL);
 273 }