specials: introduce dedicated per-object data and merge graph objects
[monky] / src / specials.c
1 /* -*- mode: c; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: t -*-
2  * vim: ts=4 sw=4 noet ai cindent syntax=c
3  *
4  * Conky, a system monitor, based on torsmo
5  *
6  * Any original torsmo code is licensed under the BSD license
7  *
8  * All code written since the fork of torsmo is licensed under the GPL
9  *
10  * Please see COPYING for details
11  *
12  * Copyright (c) 2004, Hannu Saransaari and Lauri Hakkarainen
13  * Copyright (c) 2005-2009 Brenden Matthews, Philip Kovacs, et. al.
14  *      (see AUTHORS)
15  * All rights reserved.
16  *
17  * This program is free software: you can redistribute it and/or modify
18  * it under the terms of the GNU General Public License as published by
19  * the Free Software Foundation, either version 3 of the License, or
20  * (at your option) any later version.
21  *
22  * This program is distributed in the hope that it will be useful,
23  * but WITHOUT ANY WARRANTY; without even the implied warranty of
24  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
25  * GNU General Public License for more details.
26  * You should have received a copy of the GNU General Public License
27  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
28  *
29  */
30 #include "conky.h"
31 #include "colours.h"
32 #ifdef X11
33 #include "fonts.h"
34 #endif /* X11 */
35 #include "logging.h"
36 #include "specials.h"
37 #include <math.h>
38
39 /* maximum number of special things, e.g. fonts, offsets, aligns, etc. */
40 int max_specials = MAX_SPECIALS_DEFAULT;
41
42 /* create specials array on heap instead of stack with introduction of
43  * max_specials */
44 struct special_t *specials = NULL;
45
46 int special_count;
47
48 int default_bar_width = 0, default_bar_height = 6;
49 #ifdef X11
50 int default_graph_width = 0, default_graph_height = 25;
51 int default_gauge_width = 40, default_gauge_height = 25;
52 #endif /* X11 */
53
54 /*
55  * Special data typedefs
56  */
57
58 struct bar {
59         int width, height;
60 };
61
62 /*
63  * Scanning arguments to various special text objects
64  */
65
66 #ifdef X11
67 const char *scan_gauge(const char *args, int *w, int *h)
68 {
69         /*width and height*/
70         *w = default_gauge_width;
71         *h = default_gauge_height;
72
73         /* gauge's argument is either height or height,width */
74         if (args) {
75                 int n = 0;
76
77                 if (sscanf(args, "%d,%d %n", h, w, &n) <= 1) {
78                         if (sscanf(args, "%d %n", h, &n) == 2) {
79                                 *w = *h; /*square gauge*/
80                         }
81                 }
82                 args += n;
83         }
84
85         return args;
86 }
87 #endif /* X11 */
88
89 const char *scan_bar(struct text_object *obj, const char *args)
90 {
91         struct bar *b;
92
93         b = malloc(sizeof(struct bar));
94         memset(b, 0, sizeof(struct bar));
95
96         /* zero width means all space that is available */
97         b->width = default_bar_width;
98         b->height = default_bar_height;
99         /* bar's argument is either height or height,width */
100         if (args) {
101                 int n = 0;
102
103                 if (sscanf(args, "%d,%d %n", &b->height, &b->width, &n) <= 1) {
104                         sscanf(args, "%d %n", &b->height, &n);
105                 }
106                 args += n;
107         }
108
109         obj->special_data = b;
110         return args;
111 }
112
113 #ifdef X11
114 char *scan_font(const char *args)
115 {
116         if (args && *args) {
117                 return strndup(args, DEFAULT_TEXT_BUFFER_SIZE);
118         }
119
120         return NULL;
121 }
122
123 char *scan_graph(const char *args, int *w, int *h,
124                  unsigned int *first_colour, unsigned int *last_colour,
125                  unsigned int *scale, char *showaslog, char *tempgrad)
126 {
127         char buf[1024];
128         memset(buf, 0, 1024);
129
130         /* zero width means all space that is available */
131         *w = default_graph_width;
132         *h = default_graph_height;
133         *first_colour = 0;
134         *last_colour = 0;
135         *scale = 0;
136         *tempgrad = FALSE;
137         *showaslog = FALSE;
138         if (args) {
139                 if (strstr(args, " "TEMPGRAD) || strncmp(args, TEMPGRAD, strlen(TEMPGRAD)) == 0) {
140                         *tempgrad = TRUE;
141                 }
142                 if (strstr(args, " "LOGGRAPH) || strncmp(args, LOGGRAPH, strlen(LOGGRAPH)) == 0) {
143                         *showaslog = TRUE;
144                 }
145                 if (sscanf(args, "%d,%d %x %x %u", h, w, first_colour, last_colour, scale) == 5) {
146                         return NULL;
147                 }
148                 *scale = 0;
149                 if (sscanf(args, "%d,%d %x %x", h, w, first_colour, last_colour) == 4) {
150                         return NULL;
151                 }
152                 if (sscanf(args, "%1023s %d,%d %x %x %u", buf, h, w, first_colour, last_colour, scale) == 6) {
153                         return strndup(buf, text_buffer_size);
154                 }
155                 *scale = 0;
156                 if (sscanf(args, "%1023s %d,%d %x %x", buf, h, w, first_colour, last_colour) == 5) {
157                         return strndup(buf, text_buffer_size);
158                 }
159                 buf[0] = '\0';
160                 *h = 25;
161                 *w = 0;
162                 if (sscanf(args, "%x %x %u", first_colour, last_colour, scale) == 3) {
163                         return NULL;
164                 }
165                 *scale = 0;
166                 if (sscanf(args, "%x %x", first_colour, last_colour) == 2) {
167                         return NULL;
168                 }
169                 if (sscanf(args, "%1023s %x %x %u", buf, first_colour, last_colour, scale) == 4) {
170                         return strndup(buf, text_buffer_size);
171                 }
172                 *scale = 0;
173                 if (sscanf(args, "%1023s %x %x", buf, first_colour, last_colour) == 3) {
174                         return strndup(buf, text_buffer_size);
175                 }
176                 buf[0] = '\0';
177                 *first_colour = 0;
178                 *last_colour = 0;
179                 if (sscanf(args, "%d,%d %u", h, w, scale) == 3) {
180                         return NULL;
181                 }
182                 *scale = 0;
183                 if (sscanf(args, "%d,%d", h, w) == 2) {
184                         return NULL;
185                 }
186                 if (sscanf(args, "%1023s %d,%d %u", buf, h, w, scale) < 4) {
187                         *scale = 0;
188                         //TODO: check the return value and throw an error?
189                         sscanf(args, "%1023s %d,%d", buf, h, w);
190                 }
191
192                 return strndup(buf, text_buffer_size);
193         }
194
195         if (buf[0] == '\0') {
196                 return NULL;
197         } else {
198                 return strndup(buf, text_buffer_size);
199         }
200 }
201 #endif /* X11 */
202
203 /*
204  * Printing various special text objects
205  */
206
207 static struct special_t *new_special(char *buf, enum special_types t)
208 {
209         if (special_count >= max_specials) {
210                 CRIT_ERR(NULL, NULL, "too many special things in text");
211         }
212
213         buf[0] = SPECIAL_CHAR;
214         buf[1] = '\0';
215         specials[special_count].type = t;
216         return &specials[special_count++];
217 }
218
219 #ifdef X11
220 void new_gauge(char *buf, int w, int h, int usage)
221 {
222         struct special_t *s = 0;
223         if ((output_methods & TO_X) == 0)
224                 return;
225
226         s = new_special(buf, GAUGE);
227
228         s->arg = (usage > 255) ? 255 : ((usage < 0) ? 0 : usage);
229         s->width = w;
230         s->height = h;
231 }
232
233 void new_bar(struct text_object *obj, char *buf, int usage)
234 {
235         struct special_t *s = 0;
236         struct bar *b = obj->special_data;
237
238         if ((output_methods & TO_X) == 0)
239                 return;
240
241         if (!b)
242                 return;
243
244         s = new_special(buf, BAR);
245
246         s->arg = (usage > 255) ? 255 : ((usage < 0) ? 0 : usage);
247         s->width = b->width;
248         s->height = b->height;
249 }
250
251 void new_font(char *buf, char *args)
252 {
253         if ((output_methods & TO_X) == 0)
254                 return;
255
256         if (args) {
257                 struct special_t *s = new_special(buf, FONT);
258
259                 if (s->font_added > font_count || !s->font_added || (strncmp(args, fonts[s->font_added].name, DEFAULT_TEXT_BUFFER_SIZE) != EQUAL) ) {
260                         int tmp = selected_font;
261
262                         selected_font = s->font_added = add_font(args);
263                         selected_font = tmp;
264                 }
265         } else {
266                 struct special_t *s = new_special(buf, FONT);
267                 int tmp = selected_font;
268
269                 selected_font = s->font_added = 0;
270                 selected_font = tmp;
271         }
272 }
273
274 static void graph_append(struct special_t *graph, double f, char showaslog)
275 {
276         int i;
277
278         if (showaslog) {
279 #ifdef MATH
280                 f = log10(f + 1);
281 #endif
282         }
283         
284         if (!graph->scaled && f > graph->graph_scale) {
285                 f = graph->graph_scale;
286         }
287
288         graph->graph[0] = f;    /* add new data */
289         /* shift all the data by 1 */
290         for (i = graph->graph_width - 1; i > 0; i--) {
291                 graph->graph[i] = graph->graph[i - 1];
292                 if (graph->scaled && graph->graph[i - 1] > graph->graph_scale) {
293                         /* check if we need to update the scale */
294                         graph->graph_scale = graph->graph[i - 1];
295                 }
296         }
297         if (graph->scaled && graph->graph[graph->graph_width] > graph->graph_scale) {
298                 /* check if we need to update the scale */
299                 graph->graph_scale = graph->graph[graph->graph_width];
300         }
301 }
302
303 void new_graph(char *buf, int w, int h, unsigned int first_colour,
304                 unsigned int second_colour, double i, int scale, int append, char showaslog, char tempgrad)
305 {
306         struct special_t *s = 0;
307
308         if ((output_methods & TO_X) == 0)
309                 return;
310
311         s = new_special(buf, GRAPH);
312
313         s->width = w;
314         if (s->graph == NULL) {
315                 if (s->width > 0 && s->width < MAX_GRAPH_DEPTH) {
316                         // subtract 2 for the box
317                         s->graph_width = s->width /* - 2 */;
318                 } else {
319                         s->graph_width = MAX_GRAPH_DEPTH - 2;
320                 }
321                 s->graph = malloc(s->graph_width * sizeof(double));
322                 memset(s->graph, 0, s->graph_width * sizeof(double));
323                 s->graph_scale = 100;
324         }
325         s->height = h;
326         s->first_colour = adjust_colours(first_colour);
327         s->last_colour = adjust_colours(second_colour);
328         if (scale != 0) {
329                 s->scaled = 0;
330                 s->graph_scale = scale;
331                 s->show_scale = 0;
332         } else {
333                 s->scaled = 1;
334                 s->graph_scale = 1;
335                 s->show_scale = 1;
336         }
337         s->tempgrad = tempgrad;
338         /* if (s->width) {
339                 s->graph_width = s->width - 2;  // subtract 2 for rectangle around
340         } */
341 #ifdef MATH
342         if (showaslog) {
343                 s->graph_scale = log10(s->graph_scale + 1);
344         }
345 #endif
346         if (append) {
347                 graph_append(s, i, showaslog);
348         }
349 }
350
351 void new_hr(char *buf, int a)
352 {
353         if ((output_methods & TO_X) == 0)
354                 return;
355
356         new_special(buf, HORIZONTAL_LINE)->height = a;
357 }
358
359 void new_stippled_hr(char *buf, int a, int b)
360 {
361         struct special_t *s = 0;
362
363         if ((output_methods & TO_X) == 0)
364                 return;
365
366         s = new_special(buf, STIPPLED_HR);
367
368         s->height = b;
369         s->arg = a;
370 }
371 #endif /* X11 */
372
373 void new_fg(char *buf, long c)
374 {
375 #ifdef X11
376         if (output_methods & TO_X)
377                 new_special(buf, FG)->arg = c;
378 #endif /* X11 */
379 #ifdef NCURSES
380         if (output_methods & TO_NCURSES)
381                 new_special(buf, FG)->arg = c;
382 #endif /* NCURSES */
383         UNUSED(buf);
384         UNUSED(c);
385 }
386
387 #ifdef X11
388 void new_bg(char *buf, long c)
389 {
390         if ((output_methods & TO_X) == 0)
391                 return;
392
393         new_special(buf, BG)->arg = c;
394 }
395 #endif /* X11 */
396
397 void new_bar_in_shell(struct text_object *obj, char* buffer, int buf_max_size, double usage)
398 {
399         struct bar *b = obj->special_data;
400         int width;
401
402         if (!b)
403                 return;
404
405         width = b->width;
406         if (!width)
407                 width = DEFAULT_BAR_WIDTH_NO_X;
408
409         if(width<=buf_max_size){
410                 int i = 0, j = 0, scaledusage = round_to_int( usage * width / 100);
411
412                 #ifdef HAVE_OPENMP
413                 #pragma omp parallel for schedule(dynamic,10)
414                 #endif /* HAVE_OPENMP */
415                 for(i=0; i<(int)scaledusage; i++) {
416                         *(buffer+i)='#';
417                 }
418                 /* gcc seems to think i is not initialized properly :/ */
419                 j = i;
420                 #ifdef HAVE_OPENMP
421                 #pragma omp parallel for schedule(dynamic,10)
422                 #endif /* HAVE_OPENMP */
423                 for(i = j/* cheats */; i < width; i++) {
424                         *(buffer+i)='_';
425                 }
426                 *(buffer+i)=0;
427         }
428 }
429
430 void new_outline(char *buf, long c)
431 {
432         new_special(buf, OUTLINE)->arg = c;
433 }
434
435 void new_offset(char *buf, long c)
436 {
437         new_special(buf, OFFSET)->arg = c;
438 }
439
440 void new_voffset(char *buf, long c)
441 {
442         new_special(buf, VOFFSET)->arg = c;
443 }
444
445 void new_alignr(char *buf, long c)
446 {
447         new_special(buf, ALIGNR)->arg = c;
448 }
449
450 // A postive offset pushes the text further left
451 void new_alignc(char *buf, long c)
452 {
453         new_special(buf, ALIGNC)->arg = c;
454 }
455
456 void new_goto(char *buf, long c)
457 {
458         new_special(buf, GOTO)->arg = c;
459 }
460
461 void new_tab(char *buf, int a, int b)
462 {
463         struct special_t *s = new_special(buf, TAB);
464
465         s->width = a;
466         s->arg = b;
467 }
468