(no commit message)
[psensor.git] / src / graph.c
1 /*
2     Copyright (C) 2010-2011 wpitchoune@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 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 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 int compute_y(float temp, int mint, int maxt, int width, int height)
55 {
56         double t = temp - mint;
57         return height - ((double)height * (t / (maxt - mint)));
58 }
59
60 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 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 void
97 graph_update(struct psensor **sensors,
98              GtkWidget *w_graph, struct config *config)
99 {
100         struct color *fgcolor = config->graph_fgcolor;
101         int et, bt;
102         int min_rpm = get_min_rpm(sensors);
103         int max_rpm = get_max_rpm(sensors);
104
105         int mint = get_min_temp(sensors);
106         char *strmin = psensor_value_to_string(SENSOR_TYPE_TEMP, mint);
107
108         int maxt = get_max_temp(sensors);
109         char *strmax = psensor_value_to_string(SENSOR_TYPE_TEMP, maxt);
110
111         int width = w_graph->allocation.width;
112         int height = w_graph->allocation.height;
113
114         int g_width;
115
116         /* horizontal and vertical offset of the graph */
117         int g_xoff, g_yoff;
118
119         int g_height;
120
121         cairo_surface_t *cst = cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
122                                                           width,
123                                                           height);
124         cairo_t *cr = cairo_create(cst);
125         cairo_t *cr_pixmap;
126
127         char *str_btime = time_to_str(get_graph_begin_time_s(config));
128         cairo_text_extents_t te_btime;
129
130         char *str_etime = time_to_str(get_graph_end_time_s());
131         cairo_text_extents_t te_etime;
132
133         /* setup dash style */
134         double dashes[] = {
135                 1.0,            /* ink */
136                 1.0,            /* skip */
137                 1.0,            /* ink */
138                 1.0             /* skip */
139         };
140         int ndash = sizeof(dashes) / sizeof(dashes[0]);
141
142         int i;
143
144         cairo_text_extents_t te_max, te_min;
145
146         struct psensor **sensor_cur;
147
148         draw_graph_background(cr, width, height, config);
149
150         cairo_select_font_face(cr,
151                                "sans-serif",
152                                CAIRO_FONT_SLANT_NORMAL,
153                                CAIRO_FONT_WEIGHT_NORMAL);
154         cairo_set_font_size(cr, 10.0);
155
156         cairo_text_extents(cr, str_etime, &te_etime);
157         cairo_text_extents(cr, str_btime, &te_btime);
158         cairo_text_extents(cr, strmax, &te_max);
159         cairo_text_extents(cr, strmin, &te_min);
160
161         if (te_etime.height > te_btime.height)
162                 g_yoff = GRAPH_V_PADDING + te_etime.height + GRAPH_V_PADDING;
163         else
164                 g_yoff = GRAPH_V_PADDING + te_btime.height + GRAPH_V_PADDING;
165
166         if (te_min.width > te_max.width)
167                 g_xoff = (2 * GRAPH_H_PADDING) + te_max.width;
168         else
169                 g_xoff = (2 * GRAPH_H_PADDING) + te_min.width;
170
171         g_width = width - g_xoff;
172
173         cairo_set_source_rgb(cr,
174                              fgcolor->f_red, fgcolor->f_green, fgcolor->f_blue);
175
176         g_height = height - g_yoff - GRAPH_V_PADDING;
177
178         /* draw graph begin time */
179         cairo_move_to(cr, g_xoff, height - GRAPH_V_PADDING);
180         cairo_show_text(cr, str_btime);
181         free(str_btime);
182
183         /* draw graph end time */
184         cairo_move_to(cr,
185                       width - te_etime.width - GRAPH_H_PADDING,
186                       height - GRAPH_V_PADDING);
187         cairo_show_text(cr, str_etime);
188         free(str_etime);
189
190         /* draw min and max temp */
191         cairo_move_to(cr, GRAPH_H_PADDING, te_max.height + GRAPH_V_PADDING);
192         cairo_show_text(cr, strmax);
193         free(strmax);
194
195         cairo_move_to(cr,
196                       GRAPH_H_PADDING, height - (te_min.height / 2) - g_yoff);
197         cairo_show_text(cr, strmin);
198         free(strmin);
199
200         /* draw background lines */
201         cairo_set_dash(cr, dashes, ndash, 0);
202         cairo_set_line_width(cr, 1);
203         cairo_set_source_rgb(cr,
204                              fgcolor->f_red, fgcolor->f_green, fgcolor->f_blue);
205
206         /* vertical lines representing time steps */
207         for (i = 0; i <= 5; i++) {
208                 int x = i * (g_width / 5) + g_xoff;
209                 cairo_move_to(cr, x, GRAPH_V_PADDING);
210                 cairo_line_to(cr, x, height - g_yoff);
211                 cairo_stroke(cr);
212         }
213
214         /* horizontal lines draws a line for each 10C step */
215         for (i = mint; i < maxt; i++) {
216                 if (i % 10 == 0) {
217                         int y = compute_y(i, mint, maxt, width, g_height)
218                             + GRAPH_V_PADDING;
219                         cairo_move_to(cr, g_xoff, y);
220                         cairo_line_to(cr, width - GRAPH_H_PADDING, y);
221                         cairo_stroke(cr);
222                 }
223         }
224
225         /* back to normal line style */
226         cairo_set_dash(cr, 0, 0, 0);
227
228         /* .. and finaly draws the temperature graphs */
229         bt = get_graph_begin_time_s(config);
230         et = get_graph_end_time_s();
231
232         sensor_cur = sensors;
233         while (*sensor_cur) {
234                 struct psensor *s = *sensor_cur;
235
236                 if (s->enabled) {
237                         int first = 1;
238                         int i;
239
240                         cairo_set_source_rgb(cr,
241                                              s->color->f_red,
242                                              s->color->f_green,
243                                              s->color->f_blue);
244                         cairo_set_line_width(cr, 1);
245
246                         for (i = 0; i < s->values_max_length; i++) {
247                                 int min, max, x, y, t, v;
248
249                                 t = s->measures[i].time.tv_sec;
250                                 v = s->measures[i].value;
251
252                                 if (!v || !t || !bt || !et || (t - bt) < 0)
253                                         continue;
254
255                                 if (is_fan_type(s->type)) {
256                                         min = min_rpm;
257                                         max = max_rpm;
258                                 } else {
259                                         min = mint;
260                                         max = maxt;
261                                 }
262
263                                 x = (t - bt) * g_width / (et - bt) + g_xoff;
264
265                                 y = compute_y(v, min, max, g_width, g_height)
266                                     + GRAPH_V_PADDING;
267
268                                 if (first) {
269                                         cairo_move_to(cr, x, y);
270                                         first = 0;
271                                 } else {
272                                         cairo_line_to(cr, x, y);
273                                 }
274
275                         }
276                         cairo_stroke(cr);
277                 }
278
279                 sensor_cur++;
280         }
281
282         cr_pixmap = gdk_cairo_create(w_graph->window);
283
284         if (cr_pixmap) {
285
286                 if (config->alpha_channel_enabled)
287                         cairo_set_operator(cr_pixmap, CAIRO_OPERATOR_SOURCE);
288
289                 cairo_set_source_surface(cr_pixmap, cst, 0, 0);
290                 cairo_paint(cr_pixmap);
291         }
292
293         cairo_destroy(cr_pixmap);
294         cairo_surface_destroy(cst);
295         cairo_destroy(cr);
296 }