2
* Thermal plugin to lxpanel
4
* Copyright (C) 2007 by Daniel Kesler <kesler.daniel@gmail.com>
5
* 2014 by Andriy Grytsenko <andrej@rep.kiev.ua>
7
* This program is free software; you can redistribute it and/or modify
8
* it under the terms of the GNU General Public License as published by
9
* the Free Software Foundation; either version 2 of the License, or
10
* (at your option) any later version.
12
* This program is distributed in the hope that it will be useful,
13
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15
* General Public License for more details.
17
* You should have received a copy of the GNU General Public License
18
* along with this program; if not, write to the Free Software
19
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23
#include <sys/types.h>
26
#include <glib/gi18n.h>
35
#define PROC_THERMAL_DIRECTORY "/proc/acpi/thermal_zone/" /* must be slash-terminated */
36
#define PROC_THERMAL_TEMPF "temperature"
37
#define PROC_THERMAL_TRIP "trip_points"
38
#define PROC_TRIP_CRITICAL "critical (S5):"
40
#define SYSFS_THERMAL_DIRECTORY "/sys/class/thermal/" /* must be slash-terminated */
41
#define SYSFS_THERMAL_SUBDIR_PREFIX "thermal_zone"
42
#define SYSFS_THERMAL_TEMPF "temp"
43
#define SYSFS_THERMAL_TRIP "trip_point_0_temp"
45
#define MAX_NUM_SENSORS 10
46
#define MAX_AUTOMATIC_CRITICAL_TEMP 150 /* in degrees Celsius */
48
#if !GLIB_CHECK_VERSION(2, 40, 0)
49
# define g_info(...) g_log(G_LOG_DOMAIN, G_LOG_LEVEL_INFO, __VA_ARGS__)
52
typedef gint (*GetTempFunc)(char const *);
54
typedef struct thermal {
56
config_setting_t *settings;
61
int not_custom_levels, auto_sensor;
71
char *sensor_array[MAX_NUM_SENSORS];
72
char *sensor_name[MAX_NUM_SENSORS];
73
GetTempFunc get_temperature[MAX_NUM_SENSORS];
74
GetTempFunc get_critical[MAX_NUM_SENSORS];
75
gint temperature[MAX_NUM_SENSORS];
76
gint critical[MAX_NUM_SENSORS];
81
proc_get_critical(char const* sensor_path){
83
char buf[ 256 ], sstmp [ 100 ];
86
if(sensor_path == NULL) return -1;
88
snprintf(sstmp,sizeof(sstmp),"%s%s",sensor_path,PROC_THERMAL_TRIP);
90
if (!(state = fopen( sstmp, "r"))) {
91
g_warning("thermal: cannot open %s", sstmp);
95
while( fgets(buf, 256, state) &&
96
! ( pstr = strstr(buf, PROC_TRIP_CRITICAL) ) );
99
pstr += strlen(PROC_TRIP_CRITICAL);
100
while( *pstr && *pstr == ' ' )
103
pstr[strlen(pstr)-3] = '\0';
113
proc_get_temperature(char const* sensor_path){
115
char buf[ 256 ], sstmp [ 100 ];
118
if(sensor_path == NULL) return -1;
120
snprintf(sstmp,sizeof(sstmp),"%s%s",sensor_path,PROC_THERMAL_TEMPF);
122
if (!(state = fopen( sstmp, "r"))) {
123
g_warning("thermal: cannot open %s", sstmp);
127
while( fgets(buf, 256, state) &&
128
! ( pstr = strstr(buf, "temperature:") ) );
132
while( *pstr && *pstr == ' ' )
135
pstr[strlen(pstr)-3] = '\0';
144
static gint _get_reading(const char *path, gboolean quiet)
150
if (!(state = fopen(path, "r"))) {
152
g_warning("thermal: cannot open %s", path);
156
while( fgets(buf, 256, state) &&
161
return atoi(pstr)/1000;
169
sysfs_get_critical(char const* sensor_path){
172
if(sensor_path == NULL) return -1;
174
snprintf(sstmp,sizeof(sstmp),"%s%s",sensor_path,SYSFS_THERMAL_TRIP);
176
return _get_reading(sstmp, TRUE);
180
sysfs_get_temperature(char const* sensor_path){
183
if(sensor_path == NULL) return -1;
185
snprintf(sstmp,sizeof(sstmp),"%s%s",sensor_path,SYSFS_THERMAL_TEMPF);
187
return _get_reading(sstmp, FALSE);
191
hwmon_get_critical(char const* sensor_path)
196
if(sensor_path == NULL) return -1;
198
spl = strlen(sensor_path) - 6;
199
if (spl < 17 || spl > 94)
202
snprintf(sstmp, sizeof(sstmp), "%.*s_crit", spl, sensor_path);
204
return _get_reading(sstmp, TRUE);
208
hwmon_get_temperature(char const* sensor_path)
210
if(sensor_path == NULL) return -1;
212
return _get_reading(sensor_path, FALSE);
215
static gint get_temperature(thermal *th, gint *warn)
220
for(i = 0; i < th->numsensors; i++){
221
cur = th->get_temperature[i](th->sensor_array[i]);
222
if (w == 2) ; /* already warning2 */
223
else if (th->critical[i] > 0 && cur >= th->critical[i] - 5)
225
else if ((!th->not_custom_levels || th->critical[i] < 0) &&
228
else if (w == 1) ; /* already warning1 */
229
else if (th->critical[i] > 0 && cur >= th->critical[i] - 10)
231
else if ((!th->not_custom_levels || th->critical[i] < 0) &&
236
th->temperature[i] = cur;
243
static gint get_critical(thermal *th)
245
gint min = MAX_AUTOMATIC_CRITICAL_TEMP;
248
for(i = 0; i < th->numsensors; i++){
249
th->critical[i] = th->get_critical[i](th->sensor_array[i]);
250
if (th->critical[i] > 0 && th->critical[i] < min)
251
min = th->critical[i];
258
update_display(thermal *th)
266
temp = get_temperature(th, &i);
268
color = th->cl_warning2;
270
color = th->cl_warning1;
272
color = th->cl_normal;
275
lxpanel_draw_label_text(th->panel, th->namew, "NA", TRUE, 1, TRUE);
278
snprintf(buffer, sizeof(buffer), "<span color=\"#%06x\"><b>%02d</b></span>",
279
gcolor2rgb24(&color), temp);
280
gtk_label_set_markup (GTK_LABEL(th->namew), buffer) ;
283
g_string_truncate(th->tip, 0);
285
for (i = 0; i < th->numsensors; i++){
286
g_string_append_printf(th->tip, "%s%s:\t%2d°C", separator, th->sensor_name[i], th->temperature[i]);
289
gtk_widget_set_tooltip_text(th->namew, th->tip->str);
292
static gboolean update_display_timeout(gpointer user_data)
294
if (g_source_is_destroyed(g_main_current_source()))
296
update_display(user_data);
297
return TRUE; /* repeat later */
301
add_sensor(thermal* th, char const* sensor_path, const char *sensor_name,
302
GetTempFunc get_temp, GetTempFunc get_crit)
304
if (th->numsensors + 1 > MAX_NUM_SENSORS){
305
g_warning("thermal: Too many sensors (max %d), ignoring '%s'",
306
MAX_NUM_SENSORS, sensor_path);
310
th->sensor_array[th->numsensors] = g_strdup(sensor_path);
311
th->sensor_name[th->numsensors] = g_strdup(sensor_name);
312
th->get_critical[th->numsensors] = get_crit;
313
th->get_temperature[th->numsensors] = get_temp;
316
g_debug("thermal: Added sensor %s", sensor_path);
322
* - Get the sensor directory, and store it in '*sensor'.
323
* - It is searched for in 'directory'.
324
* - Only the subdirectories starting with 'subdir_prefix' are accepted as sensors.
325
* - 'subdir_prefix' may be NULL, in which case any subdir is considered a sensor. */
327
find_sensors(thermal* th, char const* directory, char const* subdir_prefix,
328
GetTempFunc get_temp, GetTempFunc get_crit)
330
GDir *sensorsDirectory;
331
const char *sensor_name;
332
char sensor_path[100];
334
if (! (sensorsDirectory = g_dir_open(directory, 0, NULL)))
337
/* Scan the thermal_zone directory for available sensors */
338
while ((sensor_name = g_dir_read_name(sensorsDirectory))) {
339
if (sensor_name[0] == '.')
342
if (strncmp(sensor_name, subdir_prefix, strlen(subdir_prefix)) != 0)
345
snprintf(sensor_path,sizeof(sensor_path),"%s%s/", directory, sensor_name);
346
add_sensor(th, sensor_path, sensor_name, get_temp, get_crit);
348
g_dir_close(sensorsDirectory);
351
static gboolean try_hwmon_sensors(thermal* th, const char *path)
353
GDir *sensorsDirectory;
354
const char *sensor_name;
355
char sensor_path[100], buf[256];
358
if (!(sensorsDirectory = g_dir_open(path, 0, NULL)))
360
/* FIXME: do scanning hwmonX other than 0 */
362
while ((sensor_name = g_dir_read_name(sensorsDirectory)))
364
if (strncmp(sensor_name, "temp", 4) == 0 &&
365
strcmp(&sensor_name[5], "_input") == 0)
367
snprintf(sensor_path, sizeof(sensor_path), "%s/temp%c_label", path,
369
fp = fopen(sensor_path, "r");
373
if (fgets(buf, 256, fp))
375
char *pp = strchr(buf, '\n');
381
snprintf(sensor_path, sizeof(sensor_path), "%s/%s", path, sensor_name);
382
add_sensor(th, sensor_path, buf[0] ? buf : sensor_name,
383
hwmon_get_temperature, hwmon_get_critical);
386
g_dir_close(sensorsDirectory);
390
static void find_hwmon_sensors(thermal* th)
394
int i; /* sensor type num, we'll try up to 4 */
396
for (i = 0; i < 4; i++)
398
snprintf(dir_path, sizeof(dir_path), "/sys/class/hwmon/hwmon%d/device", i);
399
if (try_hwmon_sensors(th, dir_path))
401
c = strrchr(dir_path, '/');
403
try_hwmon_sensors(th, dir_path);
409
remove_all_sensors(thermal *th)
413
g_debug("thermal: Removing all sensors (%d)", th->numsensors);
415
for (i = 0; i < th->numsensors; i++)
417
g_free(th->sensor_array[i]);
418
g_free(th->sensor_name[i]);
425
check_sensors( thermal *th )
427
find_sensors(th, PROC_THERMAL_DIRECTORY, NULL, proc_get_temperature, proc_get_critical);
428
find_sensors(th, SYSFS_THERMAL_DIRECTORY, SYSFS_THERMAL_SUBDIR_PREFIX, sysfs_get_temperature, sysfs_get_critical);
429
if (th->numsensors == 0)
430
find_hwmon_sensors(th);
431
g_info("thermal: Found %d sensors", th->numsensors);
435
static gboolean applyConfig(gpointer p)
437
thermal *th = lxpanel_plugin_get_data(p);
441
if (th->str_cl_normal) gdk_color_parse(th->str_cl_normal, &th->cl_normal);
442
if (th->str_cl_warning1) gdk_color_parse(th->str_cl_warning1, &th->cl_warning1);
443
if (th->str_cl_warning2) gdk_color_parse(th->str_cl_warning2, &th->cl_warning2);
445
remove_all_sensors(th);
446
if(th->sensor == NULL) th->auto_sensor = TRUE;
447
if(th->auto_sensor) check_sensors(th);
448
else if (strncmp(th->sensor, "/sys/", 5) != 0)
449
add_sensor(th, th->sensor, th->sensor, proc_get_temperature, proc_get_critical);
450
else if (strncmp(th->sensor, "/sys/class/hwmon/", 17) != 0)
451
add_sensor(th, th->sensor, th->sensor, sysfs_get_temperature, sysfs_get_critical);
453
add_sensor(th, th->sensor, th->sensor, hwmon_get_temperature, hwmon_get_critical);
455
critical = get_critical(th);
457
if(th->not_custom_levels){
458
th->warning1 = critical - 10;
459
th->warning2 = critical - 5;
462
config_group_set_string(th->settings, "NormalColor", th->str_cl_normal);
463
config_group_set_string(th->settings, "Warning1Color", th->str_cl_warning1);
464
config_group_set_string(th->settings, "Warning2Color", th->str_cl_warning2);
465
config_group_set_int(th->settings, "AutomaticLevels", th->not_custom_levels);
466
/* TODO: clean obsolete setting
467
config_setting_remove(th->settings, "CustomLevels"); */
468
config_group_set_int(th->settings, "Warning1Temp", th->warning1);
469
config_group_set_int(th->settings, "Warning2Temp", th->warning2);
470
config_group_set_int(th->settings, "AutomaticSensor", th->auto_sensor);
471
config_group_set_string(th->settings, "Sensor", th->sensor);
476
thermal_destructor(gpointer user_data)
478
thermal *th = (thermal *)user_data;
481
remove_all_sensors(th);
482
g_string_free(th->tip, TRUE);
484
g_free(th->str_cl_normal);
485
g_free(th->str_cl_warning1);
486
g_free(th->str_cl_warning2);
487
g_source_remove(th->timer);
493
thermal_constructor(LXPanel *panel, config_setting_t *settings)
500
th = g_new0(thermal, 1);
502
th->settings = settings;
504
p = gtk_event_box_new();
505
lxpanel_plugin_set_data(p, th, thermal_destructor);
506
gtk_widget_set_has_window(p, FALSE);
507
gtk_container_set_border_width( GTK_CONTAINER(p), 2 );
509
th->namew = gtk_label_new("ww");
510
gtk_container_add(GTK_CONTAINER(p), th->namew);
512
th->tip = g_string_new(NULL);
514
/* By default, use automatic, that is, "not custom" temperature levels. If
515
* we were using custom levels, they would be 0°C at startup, so we would
516
* display in warning colors by default. */
517
th->not_custom_levels = TRUE;
519
if (config_setting_lookup_string(settings, "NormalColor", &tmp))
520
th->str_cl_normal = g_strdup(tmp);
521
if (config_setting_lookup_string(settings, "Warning1Color", &tmp))
522
th->str_cl_warning1 = g_strdup(tmp);
523
if (config_setting_lookup_string(settings, "Warning2Color", &tmp))
524
th->str_cl_warning2 = g_strdup(tmp);
525
config_setting_lookup_int(settings, "AutomaticSensor", &th->auto_sensor);
526
/* backward compatibility for wrong variable */
527
config_setting_lookup_int(settings, "CustomLevels", &th->not_custom_levels);
528
config_setting_lookup_int(settings, "AutomaticLevels", &th->not_custom_levels);
529
if (config_setting_lookup_string(settings, "Sensor", &tmp))
530
th->sensor = g_strdup(tmp);
531
config_setting_lookup_int(settings, "Warning1Temp", &th->warning1);
532
config_setting_lookup_int(settings, "Warning2Temp", &th->warning2);
534
if(!th->str_cl_normal)
535
th->str_cl_normal = g_strdup("#00ff00");
536
if(!th->str_cl_warning1)
537
th->str_cl_warning1 = g_strdup("#fff000");
538
if(!th->str_cl_warning2)
539
th->str_cl_warning2 = g_strdup("#ff0000");
543
gtk_widget_show(th->namew);
546
th->timer = g_timeout_add_seconds(3, (GSourceFunc) update_display_timeout, (gpointer)th);
551
static GtkWidget *config(LXPanel *panel, GtkWidget *p)
556
thermal *th = lxpanel_plugin_get_data(p);
557
dialog = lxpanel_generic_config_dlg(_("Temperature Monitor"),
558
panel, applyConfig, p,
559
_("Normal color"), &th->str_cl_normal, CONF_TYPE_STR,
560
_("Warning1 color"), &th->str_cl_warning1, CONF_TYPE_STR,
561
_("Warning2 color"), &th->str_cl_warning2, CONF_TYPE_STR,
562
_("Automatic sensor location"), &th->auto_sensor, CONF_TYPE_BOOL,
563
_("Sensor"), &th->sensor, CONF_TYPE_STR,
564
_("Automatic temperature levels"), &th->not_custom_levels, CONF_TYPE_BOOL,
565
_("Warning1 temperature"), &th->warning1, CONF_TYPE_INT,
566
_("Warning2 temperature"), &th->warning2, CONF_TYPE_INT,
572
FM_DEFINE_MODULE(lxpanel_gtk, thermal)
574
LXPanelPluginInit fm_module_init_lxpanel_gtk = {
575
.name = N_("Temperature Monitor"),
576
.description = N_("Display system temperature"),
578
.new_instance = thermal_constructor,
583
/* vim: set sw=4 sts=4 et : */