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