hide on startup
[psensor.git] / src / graph.c
1 /*
2     Copyright (C) 2010-2011 jeanfi@gmail.com
3
4     This program is free software; you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation; either version 2 of the License, or
7     (at your option) any later version.
8
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13
14     You should have received a copy of the GNU General Public License
15     along with this program; if not, write to the Free Software
16     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17     02110-1301 USA
18 */
19
20 #include <stdlib.h>
21 #include <sys/time.h>
22 #include <gtk/gtk.h>
23
24 #include "cfg.h"
25 #include "psensor.h"
26
27 /* horizontal padding */
28 #define GRAPH_H_PADDING 4
29 /* vertical padding */
30 #define GRAPH_V_PADDING 4
31
32 static time_t get_graph_end_time_s()
33 {
34         struct timeval tv;
35
36         if (gettimeofday(&tv, NULL) == 0)
37                 return tv.tv_sec;
38         else
39                 return 0;
40 }
41
42 static time_t get_graph_begin_time_s(struct config *cfg)
43 {
44         int ct;
45
46         ct = get_graph_end_time_s();
47
48         if (!ct)
49                 return 0;
50
51         return ct - cfg->graph_monitoring_duration * 60;
52 }
53
54 static int compute_y(double value, double min, double max, int height, int off)
55 {
56         double t = value - min;
57         return height - ((double)height * (t / (max - min))) + off;
58 }
59
60 static char *time_to_str(time_t s)
61 {
62         char *str;
63         /* note: localtime returns a static field, no free required */
64         struct tm *tm = localtime(&s);
65
66         if (!tm)
67                 return NULL;
68
69         str = malloc(6);
70         strftime(str, 6, "%H:%M", tm);
71
72         return str;
73 }
74
75 static void
76 draw_graph_background(cairo_t *cr,
77                       int width, int height, struct config *config)
78 {
79         struct color *bgcolor = config->graph_bgcolor;
80
81         /* draw background */
82         if (config->alpha_channel_enabled)
83                 cairo_set_source_rgba(cr,
84                                       bgcolor->f_red,
85                                       bgcolor->f_green,
86                                       bgcolor->f_blue, config->graph_bg_alpha);
87         else
88                 cairo_set_source_rgb(cr,
89                                      bgcolor->f_red,
90                                      bgcolor->f_green, bgcolor->f_blue);
91
92         cairo_rectangle(cr, 0, 0, width, height);
93         cairo_fill(cr);
94 }
95
96 /* setup dash style */
97 static double dashes[] = {
98         1.0,            /* ink */
99         1.0,            /* skip */
100         1.0,            /* ink */
101         1.0             /* skip */
102 };
103 static int ndash = sizeof(dashes) / sizeof(dashes[0]);
104
105 static void draw_background_lines(cairo_t *cr,
106                                   struct color *color,
107                                   int g_width, int g_height,
108                                   int g_xoff, int g_yoff,
109                                   int min, int max)
110 {
111         int i;
112
113         /* draw background lines */
114         cairo_set_dash(cr, dashes, ndash, 0);
115         cairo_set_line_width(cr, 1);
116         cairo_set_source_rgb(cr,
117                              color->f_red, color->f_green, color->f_blue);
118
119         /* vertical lines representing time steps */
120         for (i = 0; i <= 5; i++) {
121                 int x = i * (g_width / 5) + g_xoff;
122                 cairo_move_to(cr, x, g_yoff);
123                 cairo_line_to(cr, x, g_yoff + g_height);
124                 cairo_stroke(cr);
125         }
126
127         /* horizontal lines draws a line for each 10C step */
128         for (i = min; i < max; i++) {
129                 if (i % 10 == 0) {
130                         int y = compute_y(i, min, max, g_height, g_yoff);
131
132                         cairo_move_to(cr, g_xoff, y);
133                         cairo_line_to(cr, g_xoff + g_width, y);
134                         cairo_stroke(cr);
135                 }
136         }
137
138         /* back to normal line style */
139         cairo_set_dash(cr, 0, 0, 0);
140
141 }
142
143 static void draw_sensor_curve(struct psensor *s,
144                               cairo_t *cr,
145                               double min,
146                               double max,
147                               int bt,
148                               int et,
149                               int g_width,
150                               int g_height,
151                               int g_xoff,
152                               int g_yoff)
153 {
154         int first = 1;
155         int i;
156
157         cairo_set_source_rgb(cr,
158                              s->color->f_red,
159                              s->color->f_green,
160                              s->color->f_blue);
161         cairo_set_line_width(cr, 1);
162
163         for (i = 0; i < s->values_max_length; i++) {
164                 int x, y, t;
165                 double v;
166
167                 t = s->measures[i].time.tv_sec;
168                 v = s->measures[i].value;
169
170                 if (v == UNKNOWN_VALUE || !t || (t - bt) < 0)
171                         continue;
172
173                 x = (t - bt) * g_width / (et - bt) + g_xoff;
174
175                 y = compute_y(v, min, max, g_height, g_yoff);
176
177                 if (first) {
178                         cairo_move_to(cr, x, y);
179                         first = 0;
180                 } else {
181                         cairo_line_to(cr, x, y);
182                 }
183
184         }
185         cairo_stroke(cr);
186 }
187
188
189 void
190 graph_update(struct psensor **sensors,
191              GtkWidget *w_graph, struct config *config)
192 {
193         struct color *fgcolor = config->graph_fgcolor;
194         int et, bt;
195         double min_rpm = get_min_rpm(sensors);
196         double max_rpm = get_max_rpm(sensors);
197
198         double mint = get_min_temp(sensors);
199         char *strmin = psensor_value_to_string(SENSOR_TYPE_TEMP, mint);
200
201         double maxt = get_max_temp(sensors);
202         char *strmax = psensor_value_to_string(SENSOR_TYPE_TEMP, maxt);
203
204         int width, height, g_width, g_height;
205
206         /* horizontal and vertical offset of the graph */
207         int g_xoff, g_yoff;
208
209         cairo_surface_t *cst;
210         cairo_t *cr, *cr_pixmap;
211
212         char *str_btime = time_to_str(get_graph_begin_time_s(config));
213         char *str_etime = time_to_str(get_graph_end_time_s());
214
215         cairo_text_extents_t te_btime, te_etime, te_max, te_min;
216
217         struct psensor **sensor_cur;
218         GtkAllocation galloc;
219
220         if (!gtk_widget_is_drawable(w_graph))
221                 return ;
222
223         gtk_widget_get_allocation(w_graph, &galloc);
224         width = galloc.width;
225         height = galloc.height;
226
227         cst = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height);
228         cr = cairo_create(cst);
229
230         draw_graph_background(cr, width, height, config);
231
232         cairo_select_font_face(cr,
233                                "sans-serif",
234                                CAIRO_FONT_SLANT_NORMAL,
235                                CAIRO_FONT_WEIGHT_NORMAL);
236         cairo_set_font_size(cr, 10.0);
237
238         cairo_text_extents(cr, str_etime, &te_etime);
239         cairo_text_extents(cr, str_btime, &te_btime);
240         cairo_text_extents(cr, strmax, &te_max);
241         cairo_text_extents(cr, strmin, &te_min);
242
243         g_yoff = GRAPH_V_PADDING;
244
245         g_height = height - GRAPH_V_PADDING;
246         if (te_etime.height > te_btime.height)
247                 g_height -= GRAPH_V_PADDING + te_etime.height + GRAPH_V_PADDING;
248         else
249                 g_height -= GRAPH_V_PADDING + te_btime.height + GRAPH_V_PADDING;
250
251         if (te_min.width > te_max.width)
252                 g_xoff = (2 * GRAPH_H_PADDING) + te_max.width;
253         else
254                 g_xoff = (2 * GRAPH_H_PADDING) + te_min.width;
255
256         g_width = width - g_xoff - GRAPH_H_PADDING;
257
258         cairo_set_source_rgb(cr,
259                              fgcolor->f_red, fgcolor->f_green, fgcolor->f_blue);
260
261         /* draw graph begin time */
262         cairo_move_to(cr, g_xoff, height - GRAPH_V_PADDING);
263         cairo_show_text(cr, str_btime);
264         free(str_btime);
265
266         /* draw graph end time */
267         cairo_move_to(cr,
268                       width - te_etime.width - GRAPH_H_PADDING,
269                       height - GRAPH_V_PADDING);
270         cairo_show_text(cr, str_etime);
271         free(str_etime);
272
273         /* draw min and max temp */
274         cairo_move_to(cr, GRAPH_H_PADDING, te_max.height + GRAPH_V_PADDING);
275         cairo_show_text(cr, strmax);
276         free(strmax);
277
278         cairo_move_to(cr,
279                       GRAPH_H_PADDING, height - (te_min.height / 2) - g_yoff);
280         cairo_show_text(cr, strmin);
281         free(strmin);
282
283         draw_background_lines(cr, fgcolor,
284                               g_width, g_height,
285                               g_xoff, g_yoff,
286                               mint, maxt);
287
288         /* .. and finaly draws the temperature graphs */
289         bt = get_graph_begin_time_s(config);
290         et = get_graph_end_time_s();
291
292         if (bt && et) {
293                 sensor_cur = sensors;
294                 while (*sensor_cur) {
295                         struct psensor *s = *sensor_cur;
296
297                         if (s->enabled) {
298                                 double min, max;
299
300                                 if (is_fan_type(s->type)) {
301                                         min = min_rpm;
302                                         max = max_rpm;
303                                 } else {
304                                         min = mint;
305                                         max = maxt;
306                                 }
307
308                                 draw_sensor_curve(s, cr,
309                                                   min, max,
310                                                   bt, et,
311                                                   g_width, g_height,
312                                                   g_xoff, g_yoff);
313                         }
314
315                         sensor_cur++;
316                 }
317         }
318
319         cr_pixmap = gdk_cairo_create(gtk_widget_get_window(w_graph));
320
321         if (cr_pixmap) {
322
323                 if (config->alpha_channel_enabled)
324                         cairo_set_operator(cr_pixmap, CAIRO_OPERATOR_SOURCE);
325
326                 cairo_set_source_surface(cr_pixmap, cst, 0, 0);
327                 cairo_paint(cr_pixmap);
328         }
329
330         cairo_destroy(cr_pixmap);
331         cairo_surface_destroy(cst);
332         cairo_destroy(cr);
333 }