1 /*
2     GKrellM-gkfreq
3 
4     A plugin to GKrellM that displays the current CPU frequencies.
5 
6     Authors:
7     Brad Davis <brad@peakunix.net> (version 1.0)
8     Erik Kjellson <erikiiofph7@users.sourceforge.net>
9 
10     Copyright (C) 2005-2013
11 
12     This program is free software; you can redistribute it and/or modify
13     it under the terms of the GNU General Public License as published by
14     the Free Software Foundation; either version 2 of the License, or
15     (at your option) any later version.
16 
17     This program is distributed in the hope that it will be useful,
18     but WITHOUT ANY WARRANTY; without even the implied warranty of
19     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20     GNU General Public License for more details.
21 
22     You should have received a copy of the GNU General Public License
23     along with this program; if not, write to the Free Software
24     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
25 */
26 
27 /*
28  * Using code from gkx86info http://anchois.free.fr/
29  * with patches from whatdoineed2do@yahoo.co.uk
30  * and knefas@gmail.com
31  */
32 
33 #include <gkrellm2/gkrellm.h>
34 #include <math.h>
35 #include <stdio.h>
36 #include <sys/time.h>
37 #include <string.h>
38 #include <unistd.h>
39 #ifdef __FreeBSD__
40 #include <sys/sysctl.h>
41 #endif
42 
43 
44 // Version of the plugin
45 #define PLUGIN_VERSION          "2.2"
46 // My email address
47 #define EMAIL_ADDRESS           "erikiiofph7@users.sourceforge.net"
48 // Sourceforge homepage for the plugin_info_text&firstvalue
49 #define HOMEPAGE_URL            "http://sourceforge.net/projects/gkrellm-gkfreq/"
50 
51 // STYLE_NAME will be the theme subdirectory for custom images for this
52 //  plugin and it will be the gkrellmrc style name for custom settings.
53 #define	STYLE_NAME              "gkrellm-gkfreq"
54 // CONFIG_NAME would be the name in the configuration tree.
55 #define	CONFIG_NAME             "gkrellm-gkfreq"
56 // The MONITOR_CONFIG_KEYWORD is used to mark the lines in the config
57 // file that belongs to this plugin.
58 #define MONITOR_CONFIG_KEYWORD  "gkrellm-gkfreq"
59 // Variable name for mode
60 #define CFG_VAR_NAME_MODE       "mode"
61 // Max length of variable names in config file.
62 #define CFG_VAR_NAME_MAXLEN     31
63 
64 #define PLUGIN_PLACEMENT  MON_INSERT_AFTER|MON_CPU
65 
66 // Maximum number of CPU cores to support
67 #define MAX_NUM_CPU             64
68 // Spacings between the text labels
69 #define SPACING_BETWEEN_ROWS    1
70 #define SPACING_BETWEEN_COLS    0
71 
72 // Define modes - DON'T CHANGE THE NUMBERS, THEY HAVE TO BE ADDED IN NUMERIC ORDER LATER ON...
73 #define MODE_VAL_ALL            0
74 #define MODE_LBL_ALL            "Show all CPU frequencies"
75 #define MODE_VAL_MAXAVGMIN      1
76 #define MODE_LBL_MAXAVGMIN      "Show max, avg & min CPU frequency"
77 #define MODE_VAL_MAX            2
78 #define MODE_LBL_MAX            "Show maximum CPU frequency"
79 #define MODE_VAL_AVG            3
80 #define MODE_LBL_AVG            "Show average CPU frequency"
81 #define MODE_VAL_MIN            4
82 #define MODE_LBL_MIN            "Show minimum CPU frequency"
83 
84 // Max length of text on the About tab in the config window.
85 #define ABOUT_STRING_MAXLEN     350
86 
87 
88 // Definition of CPU structure
89 struct GKFreqStruct{
90     GkrellmDecal  *label_cpu;
91     gint          freq;
92     GkrellmDecal  *label_freq;
93 #ifdef __FreeBSD__
94     int           oid_freq[CTL_MAXNAME + 2];
95     size_t        oid_freq_len;
96 #endif
97 };
98 
99 static GkrellmMonitor *monitor;
100 static GkrellmPanel   *panel;
101 static gint           style_id;
102 
103 static struct    GKFreqStruct cpu[MAX_NUM_CPU];
104 static gint      num_cpu; // number of CPUs
105        GtkWidget *vbox_panel;
106        GtkWidget *comboMode;
107        gint      mode;
108 
109 // Get the CPU frequency (in MHz) for CPU number i
get_cpu_freq(gint i)110 static gint get_cpu_freq(gint i)
111 {
112 #ifdef __FreeBSD__
113   int freq;
114   size_t len = sizeof(freq);
115 
116   if (sysctl(cpu[i].oid_freq, cpu[i].oid_freq_len, &freq, &len, 0, 0) < 0)
117     return -1;
118   return freq;
119 #else
120   FILE *f;
121   gchar filename[100];
122   sprintf(filename, "/sys/devices/system/cpu/cpu%d/cpufreq/scaling_cur_freq",i);
123   if ( (f = fopen(filename, "r")) == NULL) {
124     return -1;
125   } else {
126     int freq;
127     fscanf(f, "%d", &freq );
128     fclose(f);
129     return freq/1000;
130   }
131 #endif
132 }
133 
134 // Callback function to be run when a panel is exposed for the first time.
panel_expose_event(GtkWidget * widget,GdkEventExpose * ev)135 static gint panel_expose_event(GtkWidget *widget, GdkEventExpose *ev) {
136 
137   gdk_draw_pixmap(widget->window,
138     widget->style->fg_gc[GTK_WIDGET_STATE (widget)],
139     panel->pixmap, ev->area.x, ev->area.y, ev->area.x, ev->area.y,
140     ev->area.width, ev->area.height);
141   return FALSE;
142 }
143 
144 // Redrawing the graphic components of the plugin.
update_plugin()145 static void update_plugin() {
146   gint i;
147 
148   // Get all CPU frequencies and calculate max, avg & min
149   for (i=0; i<num_cpu; i++) {
150     cpu[i].freq = get_cpu_freq(i);
151   }
152   gint freq_max = cpu[0].freq;
153   for (i=1; i<num_cpu; i++) {
154     freq_max = fmax(freq_max, cpu[i].freq);
155   }
156   gint freq_avg = 0;
157   for (i=0; i<num_cpu; i++) {
158     freq_avg += cpu[i].freq;
159   }
160   freq_avg = freq_avg/num_cpu;
161   gint freq_min = cpu[0].freq;
162   for (i=1; i<num_cpu; i++) {
163     freq_min = fmin(freq_min, cpu[i].freq);
164   }
165 
166   gchar text_freq[20];
167   // Check mode and create labels accordingly
168   if (mode == MODE_VAL_MAXAVGMIN) {
169     // Max
170     gkrellm_draw_decal_text(panel, cpu[0].label_cpu, "Max", 0);
171     sprintf(text_freq, "%dMHz", freq_max);
172     gkrellm_draw_decal_text(panel, cpu[0].label_freq, text_freq, 0);
173     // Avg
174     gkrellm_draw_decal_text(panel, cpu[1].label_cpu, "Avg", 0);
175     sprintf(text_freq, "%dMHz", freq_avg);
176     gkrellm_draw_decal_text(panel, cpu[1].label_freq, text_freq, 0);
177     // Min
178     gkrellm_draw_decal_text(panel, cpu[2].label_cpu, "Min", 0);
179     sprintf(text_freq, "%dMHz", freq_min);
180     gkrellm_draw_decal_text(panel, cpu[2].label_freq, text_freq, 0);
181 
182   }else if (mode == MODE_VAL_MAX) {
183     gkrellm_draw_decal_text(panel, cpu[0].label_cpu, "Max", 0);
184     sprintf(text_freq, "%dMHz", freq_max);
185     gkrellm_draw_decal_text(panel, cpu[0].label_freq, text_freq, 0);
186 
187   }else if (mode == MODE_VAL_AVG) {
188     gkrellm_draw_decal_text(panel, cpu[0].label_cpu, "Avg", 0);
189     sprintf(text_freq, "%dMHz", freq_avg);
190     gkrellm_draw_decal_text(panel, cpu[0].label_freq, text_freq, 0);
191 
192   }else if (mode == MODE_VAL_MIN) {
193     gkrellm_draw_decal_text(panel, cpu[0].label_cpu, "Min", 0);
194     sprintf(text_freq, "%dMHz", freq_min);
195     gkrellm_draw_decal_text(panel, cpu[0].label_freq, text_freq, 0);
196 
197   }else {
198     // Mode: Show all CPUs
199     // Step through all CPUs and redraw their components.
200     for (i=0; i<num_cpu; i++) {
201       gchar text_cpu[10];
202       sprintf(text_cpu, "CPU%d", i);
203       gkrellm_draw_decal_text(panel, cpu[i].label_cpu, text_cpu, 0);
204       sprintf(text_freq, "%dMHz", cpu[i].freq);
205       gkrellm_draw_decal_text(panel, cpu[i].label_freq, text_freq, 0);
206     }
207   }
208   gkrellm_draw_panel_layers(panel);
209 
210 }
211 
212 
213 // Initialize the graphic components of the plugin.
create_plugin(GtkWidget * vbox,gint first_create)214 static void create_plugin(GtkWidget *vbox, gint first_create) {
215 
216   gint i;
217 
218   GkrellmStyle      *style;
219   GkrellmTextstyle  *ts, *ts_alt;
220 
221    // Initialize panel if this is the first time this routine is run.
222   if (first_create) {
223     panel = gkrellm_panel_new0();
224   }
225 
226   // Save the Gtk vbox to be able to call this function in the future.
227   vbox_panel = vbox;
228 
229   // Get style
230   style = gkrellm_meter_style(style_id);
231 
232   // Get font styles, ts_alt is usually smaller than ts.
233   ts = gkrellm_meter_textstyle(style_id);
234   ts_alt = gkrellm_meter_alt_textstyle(style_id);
235 
236   // Count the number of CPUs
237   if (first_create) {
238     for (num_cpu=0; num_cpu<MAX_NUM_CPU; num_cpu++){
239 #ifdef __FreeBSD__
240       gchar mib[16];
241       snprintf(mib, sizeof(mib), "dev.cpu.%d.freq", num_cpu);
242       cpu[num_cpu].oid_freq_len = sizeof(cpu[num_cpu].oid_freq);
243       if (sysctlnametomib(mib, cpu[num_cpu].oid_freq,
244 			  &cpu[num_cpu].oid_freq_len) < 0)
245 #else
246       if (get_cpu_freq(num_cpu) < 0)
247 #endif
248       {
249         break;
250       }
251     }
252   }
253 
254   // Calculate x pos for frequency label
255   gint x;
256   if ((mode == MODE_VAL_ALL) && (num_cpu > 10)) {
257     x = gdk_string_width(gdk_font_from_description(ts_alt->font), "CPUXX") + SPACING_BETWEEN_COLS;
258   } else {
259     x = gdk_string_width(gdk_font_from_description(ts_alt->font), "CPUX") + SPACING_BETWEEN_COLS;
260   }
261 
262   // Check mode and create labels accordingly
263   gint freqs2show;
264   if ((mode == MODE_VAL_MAX) || (mode == MODE_VAL_AVG) || (mode == MODE_VAL_MIN)) {
265     freqs2show = 1;
266   } else if (mode == MODE_VAL_MAXAVGMIN) {
267     freqs2show = 3;
268   } else {
269     // Mode: MODE_VAL_ALL
270     freqs2show = num_cpu;
271   }
272   // Step through all frequencies to show
273   gint y = -1; /* y = -1 places at top margin  */
274   for (i=0; i<freqs2show; i++) {
275     // Create a decal for the cpu label
276     cpu[i].label_cpu = gkrellm_create_decal_text(panel,
277                             "CPUX", /* string used for vertical sizing */
278                             ts_alt,
279                             style,
280                             -1,     /* x = -1 places at left margin */
281                             y,
282                             -1);    /* use full width */
283     // Create a decal for the frequency label
284     cpu[i].label_freq = gkrellm_create_decal_text(panel,
285                             "XXXXHz", /* string used for vertical sizing */
286                             ts,
287                             style,
288                             x,
289                             y,
290                             -1);    /* use full width */
291     // Calculate y pos for next row
292     if (i == 0) {
293       y = cpu[i].label_cpu->y;
294     }
295     y = y + fmax(cpu[i].label_cpu->h, cpu[i].label_freq->h) + SPACING_BETWEEN_ROWS;
296   }
297 
298   // Configure the panel to hold the above created decals, and create it.
299   gkrellm_panel_configure(panel, NULL, style);
300   gkrellm_panel_create(vbox, monitor, panel);
301 
302   // Connect callback function for expose event.
303   if (first_create)
304       g_signal_connect(G_OBJECT (panel->drawing_area), "expose_event",
305               G_CALLBACK (panel_expose_event), NULL);
306 }
307 
308 
309 /* Save any configuration data we have in config lines in the format:
310      MONITOR_CONFIG_KEYWORD  config_keyword  data
311  */
save_plugin_config(FILE * f)312 static void save_plugin_config(FILE *f) {
313   // Save mode
314   fprintf(f, "%s %s %d\n", MONITOR_CONFIG_KEYWORD, CFG_VAR_NAME_MODE, mode);
315 }
316 
317 
318 /* When GKrellM is started up, load_plugin_config() is called if any
319    config lines for this plugin are found.  The lines must have been
320    saved by save_plugin_config().
321  */
load_plugin_config(gchar * config_line)322 static void load_plugin_config(gchar *config_line) {
323   gchar   config_keyword[CFG_VAR_NAME_MAXLEN+1], config_value[CFG_BUFSIZE];
324   gint    n;
325 
326   // Get the keyword and the corresponding data for this config line.
327   if ((n = sscanf(config_line, "%s %[^\n]",
328                                  config_keyword, config_value)) != 2) {
329     return;
330   }
331   // Check keyword
332   if (strcmp(config_keyword, CFG_VAR_NAME_MODE) == 0) {
333     // Load the number of visible counters
334     sscanf(config_value, "%d", &mode);
335   }
336 }
337 
338 
339 /* The apply is called whenever the user hits the OK or the Apply
340    button in the config window.
341  */
apply_plugin_config(void)342 static void apply_plugin_config(void) {
343     // Get the chosen mode
344     gchar *chosen =  gtk_combo_box_get_active_text(GTK_COMBO_BOX(comboMode));
345     if (strcmp(chosen, MODE_LBL_MAXAVGMIN) == 0) {
346       mode = MODE_VAL_MAXAVGMIN;
347     } else if (strcmp(chosen, MODE_LBL_MAX) == 0) {
348       mode = MODE_VAL_MAX;
349     } else if  (strcmp(chosen, MODE_LBL_AVG) == 0) {
350       mode = MODE_VAL_AVG;
351     } else if  (strcmp(chosen, MODE_LBL_MIN) == 0) {
352       mode = MODE_VAL_MIN;
353     } else {
354       mode = MODE_VAL_ALL;
355     }
356 
357     // Re-create all panel contents (needed if switching between ALL and something else).
358     gkrellm_panel_destroy(panel);
359     create_plugin(vbox_panel, 1);
360 }
361 
362 // Creates a tab for the plugin config window.
create_plugin_tab(GtkWidget * tab_vbox)363 static void create_plugin_tab(GtkWidget *tab_vbox) {
364     GtkWidget       *tabs;
365     GtkWidget       *text;
366     GtkWidget       *vbox;
367     GtkWidget       *table;
368 
369     // Create Gtk user config widgets.
370     tabs = gtk_notebook_new();
371     gtk_notebook_set_tab_pos(GTK_NOTEBOOK(tabs), GTK_POS_TOP);
372     gtk_box_pack_start(GTK_BOX(tab_vbox), tabs, TRUE, TRUE, 0);
373 
374     // Options tab
375     vbox  = gkrellm_gtk_framed_notebook_page(tabs, "Options");
376     table = gtk_table_new(1,     /* Number of rows */
377                           2,     /* Number of columns*/
378                           FALSE); /* TRUE = homogenous, all cells of equal size */
379     gtk_box_pack_start(GTK_BOX(vbox), table,
380                             FALSE, /* TRUE = expand */
381                             FALSE, /* TRUE = fill */
382                             0);    /* padding */
383     gtk_table_set_row_spacings(GTK_TABLE(table), 2);
384     gtk_table_set_col_spacings(GTK_TABLE(table), 2);
385     // Row 1: Mode selection
386     gtk_table_attach_defaults(GTK_TABLE(table), gtk_label_new("Mode:"),
387                             1,  /* Column number to attach left side to */
388                             2,  /* Column number to attach right side to */
389                             0,  /* Row number to attach top to */
390                             1); /* Row number to attach bottom to */
391     // Combo box options have to be added in numeric order!
392     comboMode = gtk_combo_box_new_text();
393     gtk_combo_box_append_text(GTK_COMBO_BOX(comboMode), MODE_LBL_ALL);
394     gtk_combo_box_append_text(GTK_COMBO_BOX(comboMode), MODE_LBL_MAXAVGMIN);
395     gtk_combo_box_append_text(GTK_COMBO_BOX(comboMode), MODE_LBL_MAX);
396     gtk_combo_box_append_text(GTK_COMBO_BOX(comboMode), MODE_LBL_AVG);
397     gtk_combo_box_append_text(GTK_COMBO_BOX(comboMode), MODE_LBL_MIN);
398     gtk_combo_box_set_active(GTK_COMBO_BOX(comboMode), mode);
399     gtk_table_attach_defaults(GTK_TABLE(table), comboMode,
400                             2,    /* Column number to attach left side to */
401                             3,    /* Column number to attach right side to */
402                             0,  /* Row number to attach top to */
403                             1); /* Row number to attach bottom to */
404 
405     // About tab
406     // Text that will show up in the "About" tab of the config window.
407     static gchar about_text[ABOUT_STRING_MAXLEN+1];
408     sprintf(about_text, "%s version %s\n"
409                         "A plugin to GKrellM that displays the current CPU frequencies.\n\n"
410                         "%s\n\n"
411                         "Authors:\n"
412                         "  Brad Davis <brad@peakunix.net> (version 1.0)\n"
413                         "  Erik Kjellson <%s>\n\n"
414                         "Copyright (C) 2005-2013\n"
415                         "Released under the GNU General Public License\n"
416                         ,CONFIG_NAME, PLUGIN_VERSION, HOMEPAGE_URL, EMAIL_ADDRESS);
417     vbox = gkrellm_gtk_framed_notebook_page(tabs, "About");
418     text = gkrellm_gtk_scrolled_text_view(vbox, NULL,
419                     GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
420     gkrellm_gtk_text_view_append(text, &about_text[0]);
421 }
422 
423 // Mandatory GKrellM plugin data structure
424 static GkrellmMonitor	plugin_mon = {
425 
426   CONFIG_NAME,        /* Name, for config tab.    */
427   0,                  /* Id,  0 if a plugin       */
428   create_plugin,      /* The create function      */
429   update_plugin,      /* The update function      */
430   create_plugin_tab,  /* The config tab create function   */
431   apply_plugin_config,/* Apply the config function        */
432 
433   save_plugin_config,  /* The save_plugin_config() function  */
434   load_plugin_config,  /* The load_plugin_config() function  */
435   MONITOR_CONFIG_KEYWORD, /* config keyword                     */
436 
437   NULL,               /* Undefined 2  */
438   NULL,               /* Undefined 1  */
439   NULL,               /* private      */
440 
441   PLUGIN_PLACEMENT,   /* Insert plugin before this monitor.       */
442 
443   NULL, /* Handle if a plugin, filled in by GKrellM     */
444   NULL  /* path if a plugin, filled in by GKrellM       */
445 };
446 
447 
448 // Mandatory initialization routine for the plugin.
gkrellm_init_plugin()449 GkrellmMonitor * gkrellm_init_plugin() {
450 
451   mode = MODE_VAL_ALL;
452 
453   style_id = gkrellm_add_meter_style(&plugin_mon, STYLE_NAME);
454   monitor = &plugin_mon;
455   return &plugin_mon;
456 }
457