49181f45578f57cf1ac455789950890e3c2c2d89
[lxde/lxpanel.git] / src / plugins / thermal / thermal.c
1 /**
2 * Thermal plugin to lxpanel
3 *
4 * Copyright (C) 2007 by Daniel Kesler <kesler.daniel@gmail.com>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 *
20 */
21
22 #include <sys/types.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <glib/gi18n.h>
26
27 #include <string.h>
28
29 #include "plugin.h"
30 #include "misc.h"
31
32 #include "dbg.h"
33
34 #define PROC_THERMAL_DIRECTORY "/proc/acpi/thermal_zone/" /* must be slash-terminated */
35 #define PROC_THERMAL_TEMPF "temperature"
36 #define PROC_THERMAL_TRIP "trip_points"
37 #define PROC_TRIP_CRITICAL "critical (S5):"
38
39 #define SYSFS_THERMAL_DIRECTORY "/sys/class/thermal/" /* must be slash-terminated */
40 #define SYSFS_THERMAL_SUBDIR_PREFIX "thermal_zone"
41 #define SYSFS_THERMAL_TEMPF "temp"
42 #define SYSFS_THERMAL_TRIP "trip_point_0_temp"
43
44 #define MAX_NUM_SENSORS 10
45 #define MAX_AUTOMATIC_CRITICAL_TEMP 150 /* in degrees Celsius */
46
47 typedef gint (*GetTempFunc)(char const *);
48
49 typedef struct thermal {
50 LXPanel *panel;
51 config_setting_t *settings;
52 GtkWidget *namew;
53 GString *tip;
54 int critical;
55 int warning1;
56 int warning2;
57 int not_custom_levels, auto_sensor;
58 char *sensor,
59 *str_cl_normal,
60 *str_cl_warning1,
61 *str_cl_warning2;
62 unsigned int timer;
63 GdkColor cl_normal,
64 cl_warning1,
65 cl_warning2;
66 int numsensors;
67 char *sensor_array[MAX_NUM_SENSORS];
68 char *sensor_name[MAX_NUM_SENSORS];
69 GetTempFunc get_temperature[MAX_NUM_SENSORS];
70 GetTempFunc get_critical[MAX_NUM_SENSORS];
71 gint temperature[MAX_NUM_SENSORS];
72 } thermal;
73
74
75 static gint
76 proc_get_critical(char const* sensor_path){
77 FILE *state;
78 char buf[ 256 ], sstmp [ 100 ];
79 char* pstr;
80
81 if(sensor_path == NULL) return -1;
82
83 snprintf(sstmp,sizeof(sstmp),"%s%s",sensor_path,PROC_THERMAL_TRIP);
84
85 if (!(state = fopen( sstmp, "r"))) {
86 ERR("thermal: cannot open %s\n", sstmp);
87 return -1;
88 }
89
90 while( fgets(buf, 256, state) &&
91 ! ( pstr = strstr(buf, PROC_TRIP_CRITICAL) ) );
92 if( pstr )
93 {
94 pstr += strlen(PROC_TRIP_CRITICAL);
95 while( *pstr && *pstr == ' ' )
96 ++pstr;
97
98 pstr[strlen(pstr)-3] = '\0';
99 fclose(state);
100 return atoi(pstr);
101 }
102
103 fclose(state);
104 return -1;
105 }
106
107 static gint
108 proc_get_temperature(char const* sensor_path){
109 FILE *state;
110 char buf[ 256 ], sstmp [ 100 ];
111 char* pstr;
112
113 if(sensor_path == NULL) return -1;
114
115 snprintf(sstmp,sizeof(sstmp),"%s%s",sensor_path,PROC_THERMAL_TEMPF);
116
117 if (!(state = fopen( sstmp, "r"))) {
118 ERR("thermal: cannot open %s\n", sstmp);
119 return -1;
120 }
121
122 while( fgets(buf, 256, state) &&
123 ! ( pstr = strstr(buf, "temperature:") ) );
124 if( pstr )
125 {
126 pstr += 12;
127 while( *pstr && *pstr == ' ' )
128 ++pstr;
129
130 pstr[strlen(pstr)-3] = '\0';
131 fclose(state);
132 return atoi(pstr);
133 }
134
135 fclose(state);
136 return -1;
137 }
138
139 static gint _get_reading(const char *path)
140 {
141 FILE *state;
142 char buf[256];
143 char* pstr;
144
145 if (!(state = fopen(path, "r"))) {
146 ERR("thermal: cannot open %s\n", path);
147 return -1;
148 }
149
150 while( fgets(buf, 256, state) &&
151 ! ( pstr = buf ) );
152 if( pstr )
153 {
154 fclose(state);
155 return atoi(pstr)/1000;
156 }
157
158 fclose(state);
159 return -1;
160 }
161
162 static gint
163 sysfs_get_critical(char const* sensor_path){
164 char sstmp [ 100 ];
165
166 if(sensor_path == NULL) return -1;
167
168 snprintf(sstmp,sizeof(sstmp),"%s%s",sensor_path,SYSFS_THERMAL_TRIP);
169
170 return _get_reading(sstmp);
171 }
172
173 static gint
174 sysfs_get_temperature(char const* sensor_path){
175 char sstmp [ 100 ];
176
177 if(sensor_path == NULL) return -1;
178
179 snprintf(sstmp,sizeof(sstmp),"%s%s",sensor_path,SYSFS_THERMAL_TEMPF);
180
181 return _get_reading(sstmp);
182 }
183
184 static gint
185 hwmon_get_critical(char const* sensor_path)
186 {
187 char sstmp [ 100 ];
188 int spl;
189
190 if(sensor_path == NULL) return -1;
191
192 spl = strlen(sensor_path) - 6;
193 if (spl < 17 || spl > 94)
194 return -1;
195
196 snprintf(sstmp, sizeof(sstmp), "%.*s_crit", spl, sensor_path);
197
198 return _get_reading(sstmp);
199 }
200
201 static gint
202 hwmon_get_temperature(char const* sensor_path)
203 {
204 if(sensor_path == NULL) return -1;
205
206 return _get_reading(sensor_path);
207 }
208
209 static gint get_temperature(thermal *th)
210 {
211 gint max = -273;
212 gint cur, i;
213
214 for(i = 0; i < th->numsensors; i++){
215 cur = th->get_temperature[i](th->sensor_array[i]);
216 if (cur > max)
217 max = cur;
218 th->temperature[i] = cur;
219 }
220
221 return max;
222 }
223
224 static gint get_critical(thermal *th)
225 {
226 gint min = MAX_AUTOMATIC_CRITICAL_TEMP;
227 gint cur, i;
228
229 for(i = 0; i < th->numsensors; i++){
230 cur = th->get_critical[i](th->sensor_array[i]);
231 if (cur < min)
232 min = cur;
233 }
234
235 return min;
236 }
237
238 static void
239 update_display(thermal *th)
240 {
241 char buffer [60];
242 int i;
243 int temp;
244 GdkColor color;
245 gchar *separator;
246
247 temp = get_temperature(th);
248 if(temp >= th->warning2)
249 color = th->cl_warning2;
250 else if(temp >= th->warning1)
251 color = th->cl_warning1;
252 else
253 color = th->cl_normal;
254
255 if(temp == -1)
256 lxpanel_draw_label_text(th->panel, th->namew, "NA", TRUE, 1, TRUE);
257 else
258 {
259 snprintf(buffer, sizeof(buffer), "<span color=\"#%06x\"><b>%02d</b></span>",
260 gcolor2rgb24(&color), temp);
261 gtk_label_set_markup (GTK_LABEL(th->namew), buffer) ;
262 }
263
264 g_string_truncate(th->tip, 0);
265 separator = "";
266 for (i = 0; i < th->numsensors; i++){
267 g_string_append_printf(th->tip, "%s%s:\t%2d°C", separator, th->sensor_name[i], th->temperature[i]);
268 separator = "\n";
269 }
270 gtk_widget_set_tooltip_text(th->namew, th->tip->str);
271 }
272
273 static gboolean update_display_timeout(gpointer user_data)
274 {
275 if (g_source_is_destroyed(g_main_current_source()))
276 return FALSE;
277 update_display(user_data);
278 return TRUE; /* repeat later */
279 }
280
281 static int
282 add_sensor(thermal* th, char const* sensor_path, const char *sensor_name,
283 GetTempFunc get_temp, GetTempFunc get_crit)
284 {
285 if (th->numsensors + 1 > MAX_NUM_SENSORS){
286 ERR("thermal: Too many sensors (max %d), ignoring '%s'\n",
287 MAX_NUM_SENSORS, sensor_path);
288 return -1;
289 }
290
291 th->sensor_array[th->numsensors] = g_strdup(sensor_path);
292 th->sensor_name[th->numsensors] = g_strdup(sensor_name);
293 th->get_critical[th->numsensors] = get_crit;
294 th->get_temperature[th->numsensors] = get_temp;
295 th->numsensors++;
296
297 LOG(LOG_ALL, "thermal: Added sensor %s\n", sensor_path);
298
299 return 0;
300 }
301
302 /* find_sensors():
303 * - Get the sensor directory, and store it in '*sensor'.
304 * - It is searched for in 'directory'.
305 * - Only the subdirectories starting with 'subdir_prefix' are accepted as sensors.
306 * - 'subdir_prefix' may be NULL, in which case any subdir is considered a sensor. */
307 static void
308 find_sensors(thermal* th, char const* directory, char const* subdir_prefix,
309 GetTempFunc get_temp, GetTempFunc get_crit)
310 {
311 GDir *sensorsDirectory;
312 const char *sensor_name;
313 char sensor_path[100];
314
315 if (! (sensorsDirectory = g_dir_open(directory, 0, NULL)))
316 return;
317
318 /* Scan the thermal_zone directory for available sensors */
319 while ((sensor_name = g_dir_read_name(sensorsDirectory))) {
320 if (sensor_name[0] == '.')
321 continue;
322 if (subdir_prefix) {
323 if (strncmp(sensor_name, subdir_prefix, strlen(subdir_prefix)) != 0)
324 continue;
325 }
326 snprintf(sensor_path,sizeof(sensor_path),"%s%s/", directory, sensor_name);
327 add_sensor(th, sensor_path, sensor_name, get_temp, get_crit);
328 }
329 g_dir_close(sensorsDirectory);
330 }
331
332 static void find_hwmon_sensors(thermal* th)
333 {
334 GDir *sensorsDirectory;
335 const char *sensor_name;
336 char sensor_path[100], buf[256];
337 FILE *fp;
338
339 if (!(sensorsDirectory = g_dir_open("/sys/class/hwmon/hwmon0/device/", 0, NULL)))
340 return;
341 /* FIXME: do scanning hwmonX other than 0 */
342
343 while ((sensor_name = g_dir_read_name(sensorsDirectory)))
344 {
345 if (strncmp(sensor_name, "temp", 4) == 0 &&
346 strcmp(&sensor_name[5], "_input") == 0)
347 {
348 snprintf(sensor_path, sizeof(sensor_path),
349 "/sys/class/hwmon/hwmon0/device/temp%c_label", sensor_name[4]);
350 fp = fopen(sensor_path, "r");
351 buf[0] = '\0';
352 if (fp)
353 {
354 if (fgets(buf, 256, fp))
355 {
356 char *pp = strchr(buf, '\n');
357 if (pp)
358 *pp = '\0';
359 }
360 fclose(fp);
361 }
362 snprintf(sensor_path, sizeof(sensor_path),
363 "/sys/class/hwmon/hwmon0/device/%s", sensor_name);
364 add_sensor(th, sensor_path, buf[0] ? buf : sensor_name,
365 hwmon_get_temperature, hwmon_get_critical);
366 }
367 }
368 g_dir_close(sensorsDirectory);
369 }
370
371
372 static void
373 remove_all_sensors(thermal *th)
374 {
375 int i;
376
377 LOG(LOG_ALL, "thermal: Removing all sensors (%d)\n", th->numsensors);
378
379 for (i = 0; i < th->numsensors; i++)
380 {
381 g_free(th->sensor_array[i]);
382 g_free(th->sensor_name[i]);
383 }
384
385 th->numsensors = 0;
386 }
387
388 static void
389 check_sensors( thermal *th )
390 {
391 find_sensors(th, PROC_THERMAL_DIRECTORY, NULL, proc_get_temperature, proc_get_critical);
392 find_sensors(th, SYSFS_THERMAL_DIRECTORY, SYSFS_THERMAL_SUBDIR_PREFIX, sysfs_get_temperature, sysfs_get_critical);
393 if (th->numsensors == 0)
394 find_hwmon_sensors(th);
395 LOG(LOG_INFO, "thermal: Found %d sensors\n", th->numsensors);
396 }
397
398
399 static gboolean applyConfig(gpointer p)
400 {
401 thermal *th = lxpanel_plugin_get_data(p);
402 ENTER;
403
404 if (th->str_cl_normal) gdk_color_parse(th->str_cl_normal, &th->cl_normal);
405 if (th->str_cl_warning1) gdk_color_parse(th->str_cl_warning1, &th->cl_warning1);
406 if (th->str_cl_warning2) gdk_color_parse(th->str_cl_warning2, &th->cl_warning2);
407
408 remove_all_sensors(th);
409 if(th->sensor == NULL) th->auto_sensor = TRUE;
410 if(th->auto_sensor) check_sensors(th);
411 else if (strncmp(th->sensor, "/sys/", 5) != 0)
412 add_sensor(th, th->sensor, th->sensor, proc_get_temperature, proc_get_critical);
413 else if (strncmp(th->sensor, "/sys/class/hwmon/", 17) != 0)
414 add_sensor(th, th->sensor, th->sensor, sysfs_get_temperature, sysfs_get_critical);
415 else
416 add_sensor(th, th->sensor, th->sensor, hwmon_get_temperature, hwmon_get_critical);
417
418 th->critical = get_critical(th);
419
420 if(th->not_custom_levels){
421 th->warning1 = th->critical - 10;
422 th->warning2 = th->critical - 5;
423 }
424
425 config_group_set_string(th->settings, "NormalColor", th->str_cl_normal);
426 config_group_set_string(th->settings, "Warning1Color", th->str_cl_warning1);
427 config_group_set_string(th->settings, "Warning2Color", th->str_cl_warning2);
428 config_group_set_int(th->settings, "CustomLevels", th->not_custom_levels);
429 config_group_set_int(th->settings, "Warning1Temp", th->warning1);
430 config_group_set_int(th->settings, "Warning2Temp", th->warning2);
431 config_group_set_int(th->settings, "AutomaticSensor", th->auto_sensor);
432 config_group_set_string(th->settings, "Sensor", th->sensor);
433 RET(FALSE);
434 }
435
436 static void
437 thermal_destructor(gpointer user_data)
438 {
439 thermal *th = (thermal *)user_data;
440
441 ENTER;
442 remove_all_sensors(th);
443 g_string_free(th->tip, TRUE);
444 g_free(th->sensor);
445 g_free(th->str_cl_normal);
446 g_free(th->str_cl_warning1);
447 g_free(th->str_cl_warning2);
448 g_source_remove(th->timer);
449 g_free(th);
450 RET();
451 }
452
453 static GtkWidget *
454 thermal_constructor(LXPanel *panel, config_setting_t *settings)
455 {
456 thermal *th;
457 GtkWidget *p;
458 const char *tmp;
459
460 ENTER;
461 th = g_new0(thermal, 1);
462 th->panel = panel;
463 th->settings = settings;
464
465 p = gtk_event_box_new();
466 lxpanel_plugin_set_data(p, th, thermal_destructor);
467 gtk_widget_set_has_window(p, FALSE);
468 gtk_container_set_border_width( GTK_CONTAINER(p), 2 );
469
470 th->namew = gtk_label_new("ww");
471 gtk_container_add(GTK_CONTAINER(p), th->namew);
472
473 th->tip = g_string_new(NULL);
474
475 /* By default, use automatic, that is, "not custom" temperature levels. If
476 * we were using custom levels, they would be 0°C at startup, so we would
477 * display in warning colors by default. */
478 th->not_custom_levels = TRUE;
479
480 if (config_setting_lookup_string(settings, "NormalColor", &tmp))
481 th->str_cl_normal = g_strdup(tmp);
482 if (config_setting_lookup_string(settings, "Warning1Color", &tmp))
483 th->str_cl_warning1 = g_strdup(tmp);
484 if (config_setting_lookup_string(settings, "Warning2Color", &tmp))
485 th->str_cl_warning2 = g_strdup(tmp);
486 config_setting_lookup_int(settings, "AutomaticSensor", &th->auto_sensor);
487 config_setting_lookup_int(settings, "CustomLevels", &th->not_custom_levels);
488 if (config_setting_lookup_string(settings, "Sensor", &tmp))
489 th->sensor = g_strdup(tmp);
490 config_setting_lookup_int(settings, "Warning1Temp", &th->warning1);
491 config_setting_lookup_int(settings, "Warning2Temp", &th->warning2);
492
493 if(!th->str_cl_normal)
494 th->str_cl_normal = g_strdup("#00ff00");
495 if(!th->str_cl_warning1)
496 th->str_cl_warning1 = g_strdup("#fff000");
497 if(!th->str_cl_warning2)
498 th->str_cl_warning2 = g_strdup("#ff0000");
499
500 applyConfig(p);
501
502 gtk_widget_show(th->namew);
503
504 update_display(th);
505 th->timer = g_timeout_add_seconds(3, (GSourceFunc) update_display_timeout, (gpointer)th);
506
507 RET(p);
508 }
509
510 static GtkWidget *config(LXPanel *panel, GtkWidget *p)
511 {
512 ENTER;
513
514 GtkWidget *dialog;
515 thermal *th = lxpanel_plugin_get_data(p);
516 dialog = lxpanel_generic_config_dlg(_("Temperature Monitor"),
517 panel, applyConfig, p,
518 _("Normal"), &th->str_cl_normal, CONF_TYPE_STR,
519 _("Warning1"), &th->str_cl_warning1, CONF_TYPE_STR,
520 _("Warning2"), &th->str_cl_warning2, CONF_TYPE_STR,
521 _("Automatic sensor location"), &th->auto_sensor, CONF_TYPE_BOOL,
522 _("Sensor"), &th->sensor, CONF_TYPE_STR,
523 _("Automatic temperature levels"), &th->not_custom_levels, CONF_TYPE_BOOL,
524 _("Warning1 Temperature"), &th->warning1, CONF_TYPE_INT,
525 _("Warning2 Temperature"), &th->warning2, CONF_TYPE_INT,
526 NULL);
527
528 RET(dialog);
529 }
530
531 FM_DEFINE_MODULE(lxpanel_gtk, thermal)
532
533 LXPanelPluginInit fm_module_init_lxpanel_gtk = {
534 .name = N_("Temperature Monitor"),
535 .description = N_("Display system temperature"),
536
537 .new_instance = thermal_constructor,
538 .config = config,
539 };
540
541
542 /* vim: set sw=4 sts=4 et : */