Merging upstream version 0.5.9.
[debian/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 "panel.h"
30 #include "misc.h"
31 #include "plugin.h"
32
33 #include "dbg.h"
34
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):"
39
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"
44
45
46 typedef struct thermal {
47 Plugin * plugin;
48 GtkWidget *main;
49 GtkWidget *namew;
50 GtkTooltips *tip;
51 int critical;
52 int warning1;
53 int warning2;
54 int not_custom_levels, auto_sensor;
55 char *sensor,
56 *str_cl_normal,
57 *str_cl_warning1,
58 *str_cl_warning2;
59 unsigned int timer;
60 GdkColor cl_normal,
61 cl_warning1,
62 cl_warning2;
63 gint (*get_temperature)(struct thermal *th);
64 gint (*get_critical)(struct thermal *th);
65 } thermal;
66
67
68 static gint
69 proc_get_critical(thermal *th){
70 FILE *state;
71 char buf[ 256 ], sstmp [ 100 ];
72 char* pstr;
73
74 if(th->sensor == NULL) return -1;
75
76 sprintf(sstmp,"%s%s",th->sensor,PROC_THERMAL_TRIP);
77
78 if (!(state = fopen( sstmp, "r"))) {
79 //printf("cannot open %s\n",sstmp);
80 return -1;
81 }
82
83 while( fgets(buf, 256, state) &&
84 ! ( pstr = strstr(buf, PROC_TRIP_CRITICAL) ) );
85 if( pstr )
86 {
87 pstr += strlen(PROC_TRIP_CRITICAL);
88 while( *pstr && *pstr == ' ' )
89 ++pstr;
90
91 pstr[strlen(pstr)-3] = '\0';
92 //printf("Critical: [%s]\n",pstr);
93 fclose(state);
94 return atoi(pstr);
95 }
96
97 fclose(state);
98 return -1;
99 }
100
101 static gint
102 proc_get_temperature(thermal *th){
103 FILE *state;
104 char buf[ 256 ], sstmp [ 100 ];
105 char* pstr;
106
107 if(th->sensor == NULL) return -1;
108
109 sprintf(sstmp,"%s%s",th->sensor,PROC_THERMAL_TEMPF);
110
111 if (!(state = fopen( sstmp, "r"))) {
112 //printf("cannot open %s\n",sstmp);
113 return -1;
114 }
115
116 while( fgets(buf, 256, state) &&
117 ! ( pstr = strstr(buf, "temperature:") ) );
118 if( pstr )
119 {
120 pstr += 12;
121 while( *pstr && *pstr == ' ' )
122 ++pstr;
123
124 pstr[strlen(pstr)-3] = '\0';
125 fclose(state);
126 return atoi(pstr);
127 }
128
129 fclose(state);
130 return -1;
131 }
132
133 static gint
134 sysfs_get_critical(thermal *th){
135 FILE *state;
136 char buf[ 256 ], sstmp [ 100 ];
137 char* pstr;
138
139 if(th->sensor == NULL) return -1;
140
141 sprintf(sstmp,"%s%s",th->sensor,SYSFS_THERMAL_TRIP);
142
143 if (!(state = fopen( sstmp, "r"))) {
144 //printf("cannot open %s\n",sstmp);
145 return -1;
146 }
147
148 while( fgets(buf, 256, state) &&
149 ! ( pstr = buf ) );
150 if( pstr )
151 {
152 fclose(state);
153 return atoi(pstr)/1000;
154 }
155
156 fclose(state);
157 return -1;
158 }
159
160 static gint
161 sysfs_get_temperature(thermal *th){
162 FILE *state;
163 char buf[ 256 ], sstmp [ 100 ];
164 char* pstr;
165
166 if(th->sensor == NULL) return -1;
167
168 sprintf(sstmp,"%s%s",th->sensor,SYSFS_THERMAL_TEMPF);
169
170 if (!(state = fopen( sstmp, "r"))) {
171 //printf("cannot open %s\n",sstmp);
172 return -1;
173 }
174
175 while (fgets(buf, 256, state) &&
176 ! ( pstr = buf ) );
177 if( pstr )
178 {
179 fclose(state);
180 return atoi(pstr)/1000;
181 }
182
183 fclose(state);
184 return -1;
185 }
186
187
188 static void
189 set_get_functions(thermal *th)
190 {
191 if (th->sensor && strncmp(th->sensor, "/sys/", 5) == 0){
192 th->get_temperature = sysfs_get_temperature;
193 th->get_critical = sysfs_get_critical;
194 } else {
195 th->get_temperature = proc_get_temperature;
196 th->get_critical = proc_get_critical;
197 }
198 }
199
200 static gint
201 update_display(thermal *th)
202 {
203 char buffer [60];
204 int temp = th->get_temperature(th);
205 GdkColor color;
206
207 if(temp >= th->warning2)
208 color = th->cl_warning2;
209 else if(temp >= th->warning1)
210 color = th->cl_warning1;
211 else
212 color = th->cl_normal;
213
214 ENTER;
215 if(temp == -1)
216 panel_draw_label_text(th->plugin->panel, th->namew, "NA", TRUE, TRUE);
217 else
218 {
219 sprintf(buffer, "<span color=\"#%06x\"><b>%02d</b></span>", gcolor2rgb24(&color), temp);
220 gtk_label_set_markup (GTK_LABEL(th->namew), buffer) ;
221 }
222
223 RET(TRUE);
224 }
225
226
227 /* get_sensor():
228 * - Get the sensor directory, and store it in '*sensor'.
229 * - It is searched for in 'directory'.
230 * - Only the subdirectories starting with 'subdir_prefix' are accepted as sensors.
231 * - 'subdir_prefix' may be NULL, in which case any subdir is considered a sensor. */
232 static void
233 get_sensor(char** sensor, char const* directory, char const* subdir_prefix)
234 {
235 GDir *sensorsDirectory;
236 const char *sensor_name;
237 char sensor_path[100];
238
239 if (! (sensorsDirectory = g_dir_open(directory, 0, NULL)))
240 {
241 *sensor = NULL;
242 return;
243 }
244
245 /* Scan the thermal_zone directory for available sensors */
246 while ((sensor_name = g_dir_read_name(sensorsDirectory))) {
247 if (sensor_name[0] != '.') {
248 if (subdir_prefix) {
249 if (strncmp(sensor_name, subdir_prefix, strlen(subdir_prefix)) != 0)
250 continue;
251 }
252 sprintf(sensor_path,"%s%s/", directory, sensor_name);
253 if(*sensor) {
254 g_free(*sensor);
255 *sensor = NULL;
256 }
257 *sensor = strdup(sensor_path);
258 break;
259 }
260 }
261 g_dir_close(sensorsDirectory);
262 }
263
264 static void
265 check_sensors( thermal *th )
266 {
267 if(th->sensor) {
268 g_free(th->sensor);
269 th->sensor = NULL;
270 }
271
272 get_sensor(&th->sensor, PROC_THERMAL_DIRECTORY, NULL);
273
274 if (!th->sensor)
275 get_sensor(&th->sensor, SYSFS_THERMAL_DIRECTORY, SYSFS_THERMAL_SUBDIR_PREFIX);
276
277 //printf("thermal sensor: %s\n", th->sensor);
278 }
279
280
281 static void applyConfig(Plugin* p)
282 {
283 thermal *th = p->priv;
284 ENTER;
285
286 if (th->str_cl_normal) gdk_color_parse(th->str_cl_normal, &th->cl_normal);
287 if (th->str_cl_warning1) gdk_color_parse(th->str_cl_warning1, &th->cl_warning1);
288 if (th->str_cl_warning2) gdk_color_parse(th->str_cl_warning2, &th->cl_warning2);
289
290 if(th->sensor == NULL) th->auto_sensor = TRUE;
291 if(th->auto_sensor) check_sensors(th);
292
293 set_get_functions(th);
294
295 th->critical = th->get_critical(th);
296
297 if(th->not_custom_levels){
298 th->warning1 = th->critical - 10;
299 th->warning2 = th->critical - 5;
300 }
301
302 RET();
303 }
304
305 static int
306 thermal_constructor(Plugin *p, char** fp)
307 {
308 thermal *th;
309
310 ENTER;
311 th = g_new0(thermal, 1);
312 th->plugin = p;
313 p->priv = th;
314
315 p->pwid = gtk_event_box_new();
316 GTK_WIDGET_SET_FLAGS( p->pwid, GTK_NO_WINDOW );
317 gtk_container_set_border_width( GTK_CONTAINER(p->pwid), 2 );
318
319 th->namew = gtk_label_new("ww");
320 gtk_container_add(GTK_CONTAINER(p->pwid), th->namew);
321
322 th->main = p->pwid;
323 th->tip = gtk_tooltips_new();
324
325 /* By default, use automatic, that is, "not custom" temperature levels. If
326 * we were using custom levels, they would be 0°C at startup, so we would
327 * display in warning colors by default. */
328 th->not_custom_levels = TRUE;
329
330 g_signal_connect (G_OBJECT (p->pwid), "button_press_event",
331 G_CALLBACK (plugin_button_press_event), (gpointer) p);
332
333 line s;
334 s.len = 256;
335
336 if (fp) {
337 /* Apply options */
338 while (lxpanel_get_line(fp, &s) != LINE_BLOCK_END) {
339 if (s.type == LINE_NONE) {
340 ERR( "thermal: illegal token %s\n", s.str);
341 goto error;
342 }
343 if (s.type == LINE_VAR) {
344 if (!g_ascii_strcasecmp(s.t[0], "NormalColor")){
345 th->str_cl_normal = g_strdup(s.t[1]);
346 }else if (!g_ascii_strcasecmp(s.t[0], "Warning1Color")){
347 th->str_cl_warning1 = g_strdup(s.t[1]);
348 }else if (!g_ascii_strcasecmp(s.t[0], "Warning2Color")){
349 th->str_cl_warning2 = g_strdup(s.t[1]);
350 }else if (!g_ascii_strcasecmp(s.t[0], "AutomaticSensor")){
351 th->auto_sensor= atoi(s.t[1]);
352 }else if (!g_ascii_strcasecmp(s.t[0], "CustomLevels")){
353 th->not_custom_levels= atoi(s.t[1]);
354 }else if (!g_ascii_strcasecmp(s.t[0], "Sensor")){
355 th->sensor= g_strdup(s.t[1]);
356 }else if (!g_ascii_strcasecmp(s.t[0], "Warning1Temp")){
357 th->warning1 = atoi(s.t[1]);
358 }else if (!g_ascii_strcasecmp(s.t[0], "Warning2Temp")){
359 th->warning2 = atoi(s.t[1]);
360 }else {
361 ERR( "thermal: unknown var %s\n", s.t[0]);
362 }
363 }
364 else {
365 ERR( "thermal: illegal in this context %s\n", s.str);
366 goto error;
367 }
368 }
369 }
370
371 if(!th->str_cl_normal)
372 th->str_cl_normal = g_strdup("#00ff00");
373 if(!th->str_cl_warning1)
374 th->str_cl_warning1 = g_strdup("#fff000");
375 if(!th->str_cl_warning2)
376 th->str_cl_warning2 = g_strdup("#ff0000");
377
378 applyConfig(p);
379
380 gtk_widget_show(th->namew);
381
382 update_display(th);
383 th->timer = g_timeout_add(1000, (GSourceFunc) update_display, (gpointer)th);
384
385 RET(TRUE);
386
387 error:
388 RET(FALSE);
389 }
390
391 static void config(Plugin *p, GtkWindow* parent) {
392 ENTER;
393
394 GtkWidget *dialog;
395 thermal *th = (thermal *) p->priv;
396 dialog = create_generic_config_dlg(_(p->class->name),
397 GTK_WIDGET(parent),
398 (GSourceFunc) applyConfig, (gpointer) p,
399 _("Normal"), &th->str_cl_normal, CONF_TYPE_STR,
400 _("Warning1"), &th->str_cl_warning1, CONF_TYPE_STR,
401 _("Warning2"), &th->str_cl_warning2, CONF_TYPE_STR,
402 _("Automatic sensor location"), &th->auto_sensor, CONF_TYPE_BOOL,
403 _("Sensor"), &th->sensor, CONF_TYPE_STR,
404 _("Automatic temperature levels"), &th->not_custom_levels, CONF_TYPE_BOOL,
405 _("Warning1 Temperature"), &th->warning1, CONF_TYPE_INT,
406 _("Warning2 Temperature"), &th->warning2, CONF_TYPE_INT,
407 NULL);
408 gtk_window_present(GTK_WINDOW(dialog));
409
410 RET();
411 }
412
413 static void
414 thermal_destructor(Plugin *p)
415 {
416 thermal *th = (thermal *)p->priv;
417
418 ENTER;
419 th = (thermal *) p->priv;
420 g_free(th->sensor);
421 g_free(th->str_cl_normal);
422 g_free(th->str_cl_warning1);
423 g_free(th->str_cl_warning2);
424 g_source_remove(th->timer);
425 g_free(th);
426 RET();
427 }
428
429 static void save_config( Plugin* p, FILE* fp )
430 {
431 thermal *th = (thermal *)p->priv;
432
433 lxpanel_put_str( fp, "NormalColor", th->str_cl_normal );
434 lxpanel_put_str( fp, "Warning1Color", th->str_cl_warning1 );
435 lxpanel_put_str( fp, "Warning2Color", th->str_cl_warning2 );
436 lxpanel_put_int( fp, "CustomLevels", th->not_custom_levels );
437 lxpanel_put_int( fp, "Warning1Temp", th->warning1 );
438 lxpanel_put_int( fp, "Warning2Temp", th->warning2 );
439 lxpanel_put_int( fp, "AutomaticSensor", th->auto_sensor );
440 lxpanel_put_str( fp, "Sensor", th->sensor );
441 }
442
443 PluginClass thermal_plugin_class = {
444
445 PLUGINCLASS_VERSIONING,
446
447 type : "thermal",
448 name : N_("Temperature Monitor"),
449 version: "0.6",
450 description : N_("Display system temperature"),
451
452 constructor : thermal_constructor,
453 destructor : thermal_destructor,
454 config : config,
455 save : save_config,
456 panel_configuration_changed : NULL
457 };