1 /*
2 *
3 * Conky, a system monitor, based on torsmo
4 *
5 * Any original torsmo code is licensed under the BSD license
6 *
7 * All code written since the fork of torsmo is licensed under the GPL
8 *
9 * Please see COPYING for details
10 *
11 * Copyright (c) 2004, Hannu Saransaari and Lauri Hakkarainen
12 * Copyright (c) 2005-2021 Brenden Matthews, Philip Kovacs, et. al.
13 * (see AUTHORS)
14 * All rights reserved.
15 *
16 * This program is free software: you can redistribute it and/or modify
17 * it under the terms of the GNU General Public License as published by
18 * the Free Software Foundation, either version 3 of the License, or
19 * (at your option) any later version.
20 *
21 * This program is distributed in the hope that it will be useful,
22 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 * GNU General Public License for more details.
25 * You should have received a copy of the GNU General Public License
26 * along with this program. If not, see <http://www.gnu.org/licenses/>.
27 *
28 */
29
30 #include "conky.h"
31 #include <algorithm>
32 #include <cerrno>
33 #include <climits>
34 #include <clocale>
35 #include <cmath>
36 #include <cstdarg>
37 #include <ctime>
38 #include <iostream>
39 #include <memory>
40 #include <sstream>
41 #include <string>
42 #include <vector>
43 #include "common.h"
44 #include "config.h"
45 #include "text_object.h"
46 #ifdef HAVE_DIRENT_H
47 #include <dirent.h>
48 #endif /* HAVE_DIRENT_H */
49 #include <sys/param.h>
50 #include <sys/time.h>
51 #ifdef HAVE_SYS_INOTIFY_H
52 #pragma clang diagnostic push
53 #pragma clang diagnostic ignored "-Wc99-extensions"
54 #include <sys/inotify.h>
55 #pragma clang diagnostic pop
56 #endif /* HAVE_SYS_INOTIFY_H */
57 #ifdef BUILD_X11
58 #pragma GCC diagnostic push
59 #pragma GCC diagnostic ignored "-Wvariadic-macros"
60 #include <X11/Xutil.h>
61 #ifdef BUILD_XFT
62 #include <X11/Xlib.h>
63 #endif /* BUILD_XFT */
64 #pragma GCC diagnostic pop
65 #include "x11.h"
66 #ifdef BUILD_XDAMAGE
67 #include <X11/extensions/Xdamage.h>
68 #endif
69 #ifdef BUILD_IMLIB2
70 #include "imlib2.h"
71 #endif /* BUILD_IMLIB2 */
72 #endif /* BUILD_X11 */
73 #ifdef BUILD_NCURSES
74 #include <ncurses.h>
75 #endif
76 #include <fcntl.h>
77 #include <getopt.h>
78 #include <netdb.h>
79 #include <netinet/in.h>
80 #include <sys/stat.h>
81 #include <sys/types.h>
82 #if defined BUILD_RSS
83 #include <libxml/parser.h>
84 #endif
85 #ifdef BUILD_CURL
86 #include <curl/curl.h>
87 #endif
88
89 /* local headers */
90 #include "colours.h"
91 #include "core.h"
92 #include "diskio.h"
93 #include "exec.h"
94 #ifdef BUILD_X11
95 #include "fonts.h"
96 #endif
97 #include "fs.h"
98 #ifdef BUILD_ICONV
99 #include "iconv_tools.h"
100 #endif
101 #include "llua.h"
102 #include "logging.h"
103 #include "mail.h"
104 #include "nc.h"
105 #include "net_stat.h"
106 #include "specials.h"
107 #include "temphelper.h"
108 #include "template.h"
109 #include "timeinfo.h"
110 #include "top.h"
111 #ifdef BUILD_MYSQL
112 #include "mysql.h"
113 #endif /* BUILD_MYSQL */
114 #ifdef BUILD_NVIDIA
115 #include "nvidia.h"
116 #endif
117 #ifdef BUILD_CURL
118 #include "ccurl_thread.h"
119 #endif /* BUILD_CURL */
120 #ifdef BUILD_WEATHER_METAR
121 #include "weather.h"
122 #endif /* BUILD_WEATHER_METAR */
123
124 #include "lua-config.hh"
125 #include "setting.hh"
126
127 /* check for OS and include appropriate headers */
128 #if defined(__linux__)
129 #include "linux.h"
130 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
131 #include "freebsd.h"
132 #elif defined(__DragonFly__)
133 #include "dragonfly.h"
134 #elif defined(__OpenBSD__)
135 #include "openbsd.h"
136 #endif /* __OpenBSD__ */
137 #ifdef BUILD_HTTP
138 #include <microhttpd.h>
139 #endif /* BUILD_HTTP */
140
141 #ifdef BUILD_OLD_CONFIG
142 #include "convertconf.h"
143 #endif /* BUILD_OLD_CONFIG */
144
145 #ifdef BUILD_BUILTIN_CONFIG
146 #include "defconfig.h"
147
148 #ifdef BUILD_HSV_GRADIENT
149 #include "hsv_gradient.h"
150 #endif /* BUILD_HSV_GRADIENT */
151
152 namespace {
153 const char builtin_config_magic[] = "==builtin==";
154 } // namespace
155 #endif /* BUILD_BUILTIN_CONFIG */
156
157 #ifndef S_ISSOCK
158 #define S_ISSOCK(x) ((x & S_IFMT) == S_IFSOCK)
159 #endif
160
161 #define MAX_IF_BLOCK_DEPTH 5
162
163 //#define SIGNAL_BLOCKING
164 #undef SIGNAL_BLOCKING
165
166 /* debugging level, used by logging.h */
167 int global_debug_level = 0;
168
169 /* disable inotify auto reload feature if desired */
170 static conky::simple_config_setting<bool> disable_auto_reload(
171 "disable_auto_reload", false, false);
172
173 /* two strings for internal use */
174 static char *tmpstring1, *tmpstring2;
175
176 #ifdef BUILD_NCURSES
177 extern WINDOW *ncurses_window;
178 #endif
179
180 enum spacer_state { NO_SPACER = 0, LEFT_SPACER, RIGHT_SPACER };
181 template <>
182 conky::lua_traits<spacer_state>::Map conky::lua_traits<spacer_state>::map = {
183 {"none", NO_SPACER}, {"left", LEFT_SPACER}, {"right", RIGHT_SPACER}};
184 static conky::simple_config_setting<spacer_state> use_spacer("use_spacer",
185 NO_SPACER, false);
186
187 /* variables holding various config settings */
188 static conky::simple_config_setting<bool> short_units("short_units", false,
189 true);
190 static conky::simple_config_setting<bool> format_human_readable(
191 "format_human_readable", true, true);
192
193 conky::simple_config_setting<bool> out_to_stdout("out_to_console",
194 // Default value is false, unless we are building without X
195 #ifdef BUILD_X11
196 false,
197 #else
198 true,
199 #endif
200 false);
201 static conky::simple_config_setting<bool> out_to_stderr("out_to_stderr", false,
202 false);
203
204 int top_cpu, top_mem, top_time;
205 #ifdef BUILD_IOSTATS
206 int top_io;
207 #endif
208 int top_running;
209 static conky::simple_config_setting<bool> extra_newline("extra_newline", false,
210 false);
211
212 /* Update interval */
213 conky::range_config_setting<double> update_interval(
214 "update_interval", 0.0, std::numeric_limits<double>::infinity(), 3.0, true);
215 conky::range_config_setting<double> update_interval_on_battery(
216 "update_interval_on_battery", 0.0, std::numeric_limits<double>::infinity(),
217 NOBATTERY, true);
218 conky::simple_config_setting<std::string> detect_battery("detect_battery",
219 std::string("BAT0"),
220 false);
221 static bool on_battery = false;
222
active_update_interval()223 double active_update_interval() {
224 return (on_battery ? update_interval_on_battery : update_interval)
225 .get(*state);
226 }
227
lua_setter(lua::state & l,bool init)228 void music_player_interval_setting::lua_setter(lua::state &l, bool init) {
229 lua::stack_sentry s(l, -2);
230
231 if (l.isnil(-2)) {
232 l.checkstack(1);
233 l.pushnumber(update_interval.get(l));
234 l.replace(-3);
235 }
236
237 Base::lua_setter(l, init);
238
239 ++s;
240 }
241
242 music_player_interval_setting music_player_interval;
243
244 void *global_cpu = nullptr;
245 static conky::range_config_setting<unsigned int> max_text_width(
246 "max_text_width", 0, std::numeric_limits<unsigned int>::max(), 0, true);
247
248 #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
249 extern kvm_t *kd;
250 #endif
251
252 /* prototypes for internally used functions */
253 static void signal_handler(int /*sig*/);
254 static void reload_config();
255
256 static const char *suffixes[] = {_nop("B"), _nop("KiB"), _nop("MiB"),
257 _nop("GiB"), _nop("TiB"), _nop("PiB"),
258 ""};
259
260 #ifdef BUILD_X11
261
262 static void X11_create_window();
263
264 struct _x11_stuff_s {
265 Region region;
266 #ifdef BUILD_XDAMAGE
267 Damage damage;
268 XserverRegion region2, part;
269 int event_base, error_base;
270 #endif
271 } x11_stuff;
272
273 /* text size */
274
275 static int text_start_x, text_start_y; /* text start position in window */
276 static int text_offset_x, text_offset_y; /* offset for start position */
277 static int text_width = 1,
278 text_height = 1; /* initially 1 so no zero-sized window is created */
279
280 #ifdef BUILD_XFT
281 static int xft_dpi = -1;
282 #endif /* BUILD_XFT */
283 #endif /* BUILD_X11 */
284
285 /* struct that has all info to be shared between
286 * instances of the same text object */
287 struct information info;
288
289 /* path to config file */
290 std::string current_config;
291
292 /* set to 1 if you want all text to be in uppercase */
293 static conky::simple_config_setting<bool> stuff_in_uppercase("uppercase", false,
294 true);
295
296 static conky::simple_config_setting<bool> stuff_in_lowercase("lowercase", false,
297 true);
298
299 /* Run how many times? */
300 static conky::range_config_setting<unsigned long> total_run_times(
301 "total_run_times", 0, std::numeric_limits<unsigned long>::max(), 0, true);
302
303 /* fork? */
304 static conky::simple_config_setting<bool> fork_to_background("background",
305 false, false);
306
307 /* set to 0 after the first time conky is run, so we don't fork again after the
308 * first forking */
309 int first_pass = 1;
310 int argc_copy;
311 char **argv_copy;
312
313 conky::range_config_setting<int> cpu_avg_samples("cpu_avg_samples", 1, 14, 2,
314 true);
315 conky::range_config_setting<int> net_avg_samples("net_avg_samples", 1, 14, 2,
316 true);
317 conky::range_config_setting<int> diskio_avg_samples("diskio_avg_samples", 1, 14,
318 2, true);
319
320 /* filenames for output */
321 static conky::simple_config_setting<std::string> overwrite_file(
322 "overwrite_file", std::string(), true);
323 static FILE *overwrite_fpointer = nullptr;
324 static conky::simple_config_setting<std::string> append_file("append_file",
325 std::string(),
326 true);
327 static FILE *append_fpointer = nullptr;
328
329 #ifdef BUILD_HTTP
330 #ifdef MHD_YES
331 /* older API */
332 #define MHD_Result int
333 #endif /* MHD_YES */
334 std::string webpage;
335 struct MHD_Daemon *httpd;
336 static conky::simple_config_setting<bool> http_refresh("http_refresh", false,
337 true);
338
sendanswer(void * cls,struct MHD_Connection * connection,const char * url,const char * method,const char * version,const char * upload_data,size_t * upload_data_size,void ** con_cls)339 MHD_Result sendanswer(void *cls, struct MHD_Connection *connection,
340 const char *url, const char *method, const char *version,
341 const char *upload_data, size_t *upload_data_size,
342 void **con_cls) {
343 struct MHD_Response *response = MHD_create_response_from_buffer(
344 webpage.length(), (void *)webpage.c_str(), MHD_RESPMEM_PERSISTENT);
345 MHD_Result ret = MHD_queue_response(connection, MHD_HTTP_OK, response);
346 MHD_destroy_response(response);
347 if (cls || url || method || version || upload_data || upload_data_size ||
348 con_cls) {} // make compiler happy
349 return ret;
350 }
351
352 class out_to_http_setting : public conky::simple_config_setting<bool> {
353 typedef conky::simple_config_setting<bool> Base;
354
355 protected:
lua_setter(lua::state & l,bool init)356 virtual void lua_setter(lua::state &l, bool init) {
357 lua::stack_sentry s(l, -2);
358
359 Base::lua_setter(l, init);
360
361 if (init && do_convert(l, -1).first) {
362 httpd = MHD_start_daemon(MHD_USE_SELECT_INTERNALLY, HTTPPORT, nullptr,
363 NULL, &sendanswer, nullptr, MHD_OPTION_END);
364 }
365
366 ++s;
367 }
368
cleanup(lua::state & l)369 virtual void cleanup(lua::state &l) {
370 lua::stack_sentry s(l, -1);
371
372 if (do_convert(l, -1).first) {
373 MHD_stop_daemon(httpd);
374 httpd = nullptr;
375 }
376
377 l.pop();
378 }
379
380 public:
out_to_http_setting()381 out_to_http_setting() : Base("out_to_http", false, false) {}
382 };
383 static out_to_http_setting out_to_http;
384 #endif /* BUILD_HTTP */
385
386 #ifdef BUILD_X11
387
388 static conky::simple_config_setting<bool> show_graph_scale("show_graph_scale",
389 false, false);
390 static conky::simple_config_setting<bool> show_graph_range("show_graph_range",
391 false, false);
392
393 /* Position on the screen */
394 static conky::simple_config_setting<int> gap_x("gap_x", 5, true);
395 static conky::simple_config_setting<int> gap_y("gap_y", 60, true);
396
397 /* border */
398 static conky::simple_config_setting<bool> draw_borders("draw_borders", false,
399 false);
400 static conky::simple_config_setting<bool> draw_graph_borders(
401 "draw_graph_borders", true, false);
402
403 conky::range_config_setting<char> stippled_borders(
404 "stippled_borders", 0, std::numeric_limits<char>::max(), 0, true);
405
406 static conky::simple_config_setting<bool> draw_shades("draw_shades", true,
407 false);
408 static conky::simple_config_setting<bool> draw_outline("draw_outline", false,
409 false);
410
411 #ifdef OWN_WINDOW
412 /* fixed size/pos is set if wm/user changes them */
413 static int fixed_size = 0, fixed_pos = 0;
414 #endif
415
416 static conky::range_config_setting<int> minimum_height(
417 "minimum_height", 0, std::numeric_limits<int>::max(), 5, true);
418 static conky::range_config_setting<int> minimum_width(
419 "minimum_width", 0, std::numeric_limits<int>::max(), 5, true);
420 static conky::range_config_setting<int> maximum_width(
421 "maximum_width", 0, std::numeric_limits<int>::max(), 0, true);
422
isutf8(const char * envvar)423 static bool isutf8(const char *envvar) {
424 char *s = getenv(envvar);
425 if (s != nullptr) {
426 std::string temp = s;
427 std::transform(temp.begin(), temp.end(), temp.begin(), ::tolower);
428 if ((temp.find("utf-8") != std::string::npos) ||
429 (temp.find("utf8") != std::string::npos)) {
430 return true;
431 }
432 }
433 return false;
434 }
435
436 /* UTF-8 */
437 conky::simple_config_setting<bool> utf8_mode("override_utf8_locale",
438 isutf8("LC_ALL") ||
439 isutf8("LC_CTYPE") ||
440 isutf8("LANG"),
441 false);
442
443 #endif /* BUILD_X11 */
444
445 /* maximum size of config TEXT buffer, i.e. below TEXT line. */
446 conky::range_config_setting<unsigned int> max_user_text(
447 "max_user_text", 47, std::numeric_limits<unsigned int>::max(),
448 MAX_USER_TEXT_DEFAULT, false);
449
450 /* maximum size of individual text buffers, ie $exec buffer size */
451 conky::range_config_setting<unsigned int> text_buffer_size(
452 "text_buffer_size", DEFAULT_TEXT_BUFFER_SIZE,
453 std::numeric_limits<unsigned int>::max(), DEFAULT_TEXT_BUFFER_SIZE, false);
454
455 /* pad percentages to decimals? */
456 static conky::simple_config_setting<int> pad_percents("pad_percents", 0, false);
457
458 static char *global_text = nullptr;
459
get_global_text()460 char *get_global_text() { return global_text; }
461
462 long global_text_lines;
463
464 static int total_updates;
465 static int updatereset;
466
467 std::unique_ptr<lua::state> state;
468
set_updatereset(int i)469 void set_updatereset(int i) { updatereset = i; }
470
get_updatereset()471 int get_updatereset() { return updatereset; }
472
get_total_updates()473 int get_total_updates() { return total_updates; }
474
calc_text_width(const char * s)475 int calc_text_width(const char *s) {
476 size_t slen = strlen(s);
477
478 #ifdef BUILD_X11
479 if (!out_to_x.get(*state)) {
480 #endif /* BUILD_X11 */
481 return slen;
482 #ifdef BUILD_X11
483 }
484 #ifdef BUILD_XFT
485 if (use_xft.get(*state)) {
486 XGlyphInfo gi;
487
488 if (utf8_mode.get(*state)) {
489 XftTextExtentsUtf8(display, fonts[selected_font].xftfont,
490 reinterpret_cast<const FcChar8 *>(s), slen, &gi);
491 } else {
492 XftTextExtents8(display, fonts[selected_font].xftfont,
493 reinterpret_cast<const FcChar8 *>(s), slen, &gi);
494 }
495 return gi.xOff;
496 }
497 #endif /* BUILD_XFT */
498
499 return XTextWidth(fonts[selected_font].font, s, slen);
500
501 #endif /* BUILD_X11 */
502 }
503
xft_dpi_scale(int value)504 int xft_dpi_scale(int value) {
505 #if defined(BUILD_X11) && defined(BUILD_XFT)
506 if (use_xft.get(*state) && xft_dpi > 0) {
507 return (value * xft_dpi + (value > 0 ? 48 : -48)) / 96;
508 } else {
509 return value;
510 }
511 #else /* defined(BUILD_X11) && defined(BUILD_XFT) */
512 return value;
513 #endif /* defined(BUILD_X11) && defined(BUILD_XFT) */
514 }
515
516 /* formatted text to render on screen, generated in generate_text(),
517 * drawn in draw_stuff() */
518
519 static char *text_buffer;
520
521 /* quite boring functions */
522
for_each_line(char * b,int f (char *,int))523 static inline void for_each_line(char *b, int f(char *, int)) {
524 char *ps, *pe;
525 int special_index = 0; /* specials index */
526
527 if (b == nullptr) { return; }
528 for (ps = b, pe = b; *pe != 0; pe++) {
529 if (*pe == '\n') {
530 *pe = '\0';
531 special_index = f(ps, special_index);
532 *pe = '\n';
533 ps = pe + 1;
534 }
535 }
536
537 if (ps < pe) { f(ps, special_index); }
538 }
539
convert_escapes(char * buf)540 static void convert_escapes(char *buf) {
541 char *p = buf, *s = buf;
542
543 while (*s != 0) {
544 if (*s == '\\') {
545 s++;
546 if (*s == 'n') {
547 *p++ = '\n';
548 } else if (*s == '\\') {
549 *p++ = '\\';
550 }
551 s++;
552 } else {
553 *p++ = *s++;
554 }
555 }
556 *p = '\0';
557 }
558
559 /* Prints anything normally printed with snprintf according to the current value
560 * of use_spacer. Actually slightly more flexible than snprintf, as you can
561 * safely specify the destination buffer as one of your inputs. */
spaced_print(char * buf,int size,const char * format,int width,...)562 int spaced_print(char *buf, int size, const char *format, int width, ...) {
563 int len = 0;
564 va_list argp;
565 char *tempbuf;
566
567 if (size < 1) { return 0; }
568 tempbuf = new char[size];
569
570 // Passes the varargs along to vsnprintf
571 va_start(argp, width);
572 vsnprintf(tempbuf, size, format, argp);
573 va_end(argp);
574
575 switch (use_spacer.get(*state)) {
576 case NO_SPACER:
577 len = snprintf(buf, size, "%s", tempbuf);
578 break;
579 case LEFT_SPACER:
580 len = snprintf(buf, size, "%*s", width, tempbuf);
581 break;
582 case RIGHT_SPACER:
583 len = snprintf(buf, size, "%-*s", width, tempbuf);
584 break;
585 }
586 delete[] tempbuf;
587 return len;
588 }
589
590 /* print percentage values
591 *
592 * - i.e., unsigned values between 0 and 100
593 * - respect the value of pad_percents */
percent_print(char * buf,int size,unsigned value)594 int percent_print(char *buf, int size, unsigned value) {
595 return spaced_print(buf, size, "%u", pad_percents.get(*state), value);
596 }
597
598 /* converts from bytes to human readable format (K, M, G, T)
599 *
600 * The algorithm always divides by 1024, as unit-conversion of byte
601 * counts suggests. But for output length determination we need to
602 * compare with 1000 here, as we print in decimal form. */
human_readable(long long num,char * buf,int size)603 void human_readable(long long num, char *buf, int size) {
604 const char **suffix = suffixes;
605 float fnum;
606 int precision;
607 int width;
608 const char *format;
609
610 /* Possibly just output as usual, for example for stdout usage */
611 if (!format_human_readable.get(*state)) {
612 spaced_print(buf, size, "%lld", 6, num);
613 return;
614 }
615 if (short_units.get(*state)) {
616 width = 6;
617 format = "%.*f %.1s";
618 } else {
619 width = 8;
620 format = "%.*f %-.3s";
621 }
622
623 if (llabs(num) < 1000LL) {
624 spaced_print(buf, size, format, width, 0, static_cast<float>(num),
625 _(*suffix));
626 return;
627 }
628
629 while (llabs(num / 1024) >= 1000LL && (**(suffix + 2) != 0)) {
630 num /= 1024;
631 suffix++;
632 }
633
634 suffix++;
635 fnum = num / 1024.0;
636
637 /* fnum should now be < 1000, so looks like 'AAA.BBBBB'
638 *
639 * The goal is to always have a significance of 3, by
640 * adjusting the decimal part of the number. Sample output:
641 * 123MiB
642 * 23.4GiB
643 * 5.12B
644 * so the point of alignment resides between number and unit. The
645 * upside of this is that there is minimal padding necessary, though
646 * there should be a way to make alignment take place at the decimal
647 * dot (then with fixed width decimal part).
648 *
649 * Note the repdigits below: when given a precision value, printf()
650 * rounds the float to it, not just cuts off the remaining digits. So
651 * e.g. 99.95 with a precision of 1 gets 100.0, which again should be
652 * printed with a precision of 0. Yay. */
653
654 precision = 0; /* print 100-999 without decimal part */
655 if (fnum < 99.95) { precision = 1; /* print 10-99 with one decimal place */ }
656 if (fnum < 9.995) { precision = 2; /* print 0-9 with two decimal places */ }
657
658 spaced_print(buf, size, format, width, precision, fnum, _(*suffix));
659 }
660
661 /* global object list root element */
662 static struct text_object global_root_object;
663
664 static long current_text_color;
665
set_current_text_color(long colour)666 void set_current_text_color(long colour) { current_text_color = colour; }
667
get_current_text_color()668 long get_current_text_color() { return current_text_color; }
669
extract_variable_text(const char * p)670 static void extract_variable_text(const char *p) {
671 free_text_objects(&global_root_object);
672 delete_block_and_zero(tmpstring1);
673 delete_block_and_zero(tmpstring2);
674 delete_block_and_zero(text_buffer);
675
676 extract_variable_text_internal(&global_root_object, p);
677 }
678
parse_conky_vars(struct text_object * root,const char * txt,char * p,int p_max_size)679 void parse_conky_vars(struct text_object *root, const char *txt, char *p,
680 int p_max_size) {
681 extract_variable_text_internal(root, txt);
682 generate_text_internal(p, p_max_size, *root);
683 }
684
685 /* IFBLOCK jumping algorithm
686 *
687 * This is easier as it looks like:
688 * - each IF checks it's condition
689 * - on FALSE: jump
690 * - on TRUE: don't care
691 * - each ELSE jumps unconditionally
692 * - each ENDIF is silently being ignored
693 *
694 * Why this works (or: how jumping works):
695 * Jumping means to overwrite the "obj" variable of the loop and set it to the
696 * target (i.e. the corresponding ELSE or ENDIF). After that, the for-loop does
697 * the rest: as regularly, "obj" is being updated to point to obj->next, so
698 * object parsing continues right after the corresponding ELSE or ENDIF. This
699 * means that if we find an ELSE, it's corresponding IF must not have jumped,
700 * so we need to jump always. If we encounter an ENDIF, it's corresponding IF
701 * or ELSE has not jumped, and there is nothing to do.
702 */
generate_text_internal(char * p,int p_max_size,struct text_object root)703 void generate_text_internal(char *p, int p_max_size, struct text_object root) {
704 struct text_object *obj;
705 size_t a;
706
707 if (p == nullptr) { return; }
708
709 #ifdef BUILD_ICONV
710 char *buff_in;
711
712 buff_in = new char[p_max_size];
713 memset(buff_in, 0, p_max_size);
714 #endif /* BUILD_ICONV */
715
716 p[0] = 0;
717 obj = root.next;
718 while ((obj != nullptr) && p_max_size > 0) {
719 /* check callbacks for existence and act accordingly */
720 if (obj->callbacks.print != nullptr) {
721 (*obj->callbacks.print)(obj, p, p_max_size);
722 } else if (obj->callbacks.iftest != nullptr) {
723 if ((*obj->callbacks.iftest)(obj) == 0) {
724 DBGP2("jumping");
725 if (obj->ifblock_next != nullptr) { obj = obj->ifblock_next; }
726 }
727 } else if (obj->callbacks.barval != nullptr) {
728 new_bar(obj, p, p_max_size, (*obj->callbacks.barval)(obj));
729 } else if (obj->callbacks.gaugeval != nullptr) {
730 new_gauge(obj, p, p_max_size, (*obj->callbacks.gaugeval)(obj));
731 #ifdef BUILD_X11
732 } else if (obj->callbacks.graphval != nullptr) {
733 new_graph(obj, p, p_max_size, (*obj->callbacks.graphval)(obj));
734 #endif /* BUILD_X11 */
735 } else if (obj->callbacks.percentage != nullptr) {
736 percent_print(p, p_max_size, (*obj->callbacks.percentage)(obj));
737 }
738
739 a = strlen(p);
740 #ifdef BUILD_ICONV
741 iconv_convert(&a, buff_in, p, p_max_size);
742 #endif /* BUILD_ICONV */
743 p += a;
744 p_max_size -= a;
745 (*p) = 0;
746
747 obj = obj->next;
748 }
749 #ifdef BUILD_X11
750 /* load any new fonts we may have had */
751 load_fonts(utf8_mode.get(*state));
752 #endif /* BUILD_X11 */
753 #ifdef BUILD_ICONV
754 delete[] buff_in;
755 #endif /* BUILD_ICONV */
756 }
757
evaluate(const char * text,char * p,int p_max_size)758 void evaluate(const char *text, char *p, int p_max_size) {
759 struct text_object subroot {};
760
761 /**
762 * Consider expressions like: ${execp echo '${execp echo hi}'}
763 * These would require run extract_variable_text_internal() before
764 * callbacks and generate_text_internal() after callbacks.
765 */
766 extract_variable_text_internal(&subroot, text);
767 generate_text_internal(p, p_max_size, subroot);
768 DBGP2("evaluated '%s' to '%s'", text, p);
769
770 free_text_objects(&subroot);
771 }
772
773 double current_update_time, next_update_time, last_update_time;
774
generate_text()775 static void generate_text() {
776 char *p;
777 unsigned int i, j, k;
778 special_count = 0;
779
780 current_update_time = get_time();
781
782 /* clears netstats info, calls conky::run_all_callbacks(), and changes
783 * some info.mem entries */
784 update_stuff();
785
786 /* populate the text buffer; generate_text_internal() iterates through
787 * global_root_object (an instance of the text_object struct) and calls
788 * any callbacks that were set on startup by construct_text_object(). */
789 p = text_buffer;
790
791 generate_text_internal(p, max_user_text.get(*state), global_root_object);
792 unsigned int mw = max_text_width.get(*state);
793 unsigned int tbs = text_buffer_size.get(*state);
794 if (mw > 0) {
795 for (i = 0, j = 0; p[i] != 0; i++) {
796 if (p[i] == '\n') {
797 j = 0;
798 } else if (j == mw) {
799 k = i + strlen(p + i) + 1;
800 if (k < tbs) {
801 while (k != i) {
802 p[k] = p[k - 1];
803 k--;
804 }
805 p[k] = '\n';
806 j = 0;
807 } else {
808 NORM_ERR(
809 "The end of the text_buffer is reached, increase "
810 "\"text_buffer_size\"");
811 }
812 } else {
813 j++;
814 }
815 }
816 }
817
818 if (stuff_in_uppercase.get(*state)) {
819 char *tmp_p;
820
821 tmp_p = text_buffer;
822 while (*tmp_p != 0) {
823 *tmp_p = toupper(static_cast<unsigned char>(*tmp_p));
824 tmp_p++;
825 }
826 } else if (stuff_in_lowercase.get(*state)) {
827 char *tmp_p;
828
829 tmp_p = text_buffer;
830 while (*tmp_p != 0) {
831 *tmp_p = tolower(static_cast<unsigned char>(*tmp_p));
832 tmp_p++;
833 }
834 }
835
836 double ui = active_update_interval();
837 double time = get_time();
838 next_update_time += ui;
839 if (next_update_time < time || next_update_time > time + ui) {
840 next_update_time = time - fmod(time, ui) + ui;
841 }
842 last_update_time = current_update_time;
843 total_updates++;
844 }
845
get_string_width(const char * s)846 int get_string_width(const char *s) { return *s != 0 ? calc_text_width(s) : 0; }
847
848 #ifdef BUILD_X11
get_border_total()849 static inline int get_border_total() {
850 return xft_dpi_scale(border_inner_margin.get(*state)) +
851 xft_dpi_scale(border_outer_margin.get(*state)) +
852 xft_dpi_scale(border_width.get(*state));
853 }
854
get_string_width_special(char * s,int special_index)855 static int get_string_width_special(char *s, int special_index) {
856 char *p, *final;
857 special_t *current = specials;
858 int idx = 1;
859 int width = 0;
860 long i;
861
862 if (s == nullptr) { return 0; }
863
864 if (!out_to_x.get(*state)) { return strlen(s); }
865
866 p = strndup(s, text_buffer_size.get(*state));
867 final = p;
868
869 for (i = 0; i < special_index; i++) { current = current->next; }
870 for (i = 0; i < idx; i++) { current = current->next; }
871
872 while (*p != 0) {
873 if (*p == SPECIAL_CHAR) {
874 /* shift everything over by 1 so that the special char
875 * doesn't mess up the size calculation */
876 for (i = 0; i < static_cast<long>(strlen(p)); i++) {
877 *(p + i) = *(p + i + 1);
878 }
879 if (current->type == GRAPH || current->type == GAUGE ||
880 current->type == BAR) {
881 width += current->width;
882 }
883 if (current->type == FONT) {
884 // put all following text until the next fontchange/stringend in
885 // influenced_by_font but do not include specials
886 char *influenced_by_font = strdup(p);
887 special_t *current_after_font = current;
888 for (i = 0; influenced_by_font[i] != 0; i++) {
889 if (influenced_by_font[i] == SPECIAL_CHAR) {
890 // remove specials and stop at fontchange
891 current_after_font = current_after_font->next;
892 if (current_after_font->type == FONT) {
893 influenced_by_font[i] = 0;
894 break;
895 }
896 {
897 memmove(&influenced_by_font[i], &influenced_by_font[i + 1],
898 strlen(&influenced_by_font[i + 1]) + 1);
899 }
900 }
901 }
902 // add the length of influenced_by_font in the new font to width
903 int orig_font = selected_font;
904 selected_font = current->font_added;
905 width += calc_text_width(influenced_by_font);
906 selected_font = orig_font;
907 free(influenced_by_font);
908 // make sure there chars counted in the new font are not again counted
909 // in the old font
910 int specials_skipped = 0;
911 while (i > 0) {
912 if (p[specials_skipped] != 1) {
913 memmove(&p[specials_skipped], &p[specials_skipped + 1],
914 strlen(&p[specials_skipped + 1]) + 1);
915 } else {
916 specials_skipped++;
917 }
918 i--;
919 }
920 }
921 idx++;
922 current = current->next;
923 } else {
924 p++;
925 }
926 }
927 if (strlen(final) > 1) { width += calc_text_width(final); }
928 free(final);
929 return width;
930 }
931
932 static int text_size_updater(char *s, int special_index);
933
934 int last_font_height;
update_text_area()935 static void update_text_area() {
936 int x = 0, y = 0;
937
938 if (!out_to_x.get(*state)) { return; }
939 /* update text size if it isn't fixed */
940 #ifdef OWN_WINDOW
941 if (fixed_size == 0)
942 #endif
943 {
944 text_width = xft_dpi_scale(minimum_width.get(*state));
945 text_height = 0;
946 last_font_height = font_height();
947 for_each_line(text_buffer, text_size_updater);
948 text_width += 1;
949 if (text_height < xft_dpi_scale(minimum_height.get(*state))) {
950 text_height = xft_dpi_scale(minimum_height.get(*state));
951 }
952 int mw = xft_dpi_scale(maximum_width.get(*state));
953 if (text_width > mw && mw > 0) { text_width = mw; }
954 }
955
956 alignment align = text_alignment.get(*state);
957 /* get text position on workarea */
958 switch (align) {
959 case TOP_LEFT:
960 case TOP_RIGHT:
961 case TOP_MIDDLE:
962 y = workarea[1] + xft_dpi_scale(gap_y.get(*state));
963 break;
964
965 case BOTTOM_LEFT:
966 case BOTTOM_RIGHT:
967 case BOTTOM_MIDDLE:
968 default:
969 y = workarea[3] - text_height - xft_dpi_scale(gap_y.get(*state));
970 break;
971
972 case MIDDLE_LEFT:
973 case MIDDLE_RIGHT:
974 case MIDDLE_MIDDLE:
975 y = workarea[1] + (workarea[3] - workarea[1]) / 2 - text_height / 2 -
976 xft_dpi_scale(gap_y.get(*state));
977 break;
978 }
979 switch (align) {
980 case TOP_LEFT:
981 case BOTTOM_LEFT:
982 case MIDDLE_LEFT:
983 default:
984 x = workarea[0] + xft_dpi_scale(gap_x.get(*state));
985 break;
986
987 case TOP_RIGHT:
988 case BOTTOM_RIGHT:
989 case MIDDLE_RIGHT:
990 x = workarea[2] - text_width - xft_dpi_scale(gap_x.get(*state));
991 break;
992
993 case TOP_MIDDLE:
994 case BOTTOM_MIDDLE:
995 case MIDDLE_MIDDLE:
996 x = workarea[0] + (workarea[2] - workarea[0]) / 2 - text_width / 2 -
997 xft_dpi_scale(gap_x.get(*state));
998 break;
999 }
1000 #ifdef OWN_WINDOW
1001 if (align == NONE) { // Let the WM manage the window
1002 x = window.x;
1003 y = window.y;
1004
1005 fixed_pos = 1;
1006 fixed_size = 1;
1007 }
1008 #endif /* OWN_WINDOW */
1009 #ifdef OWN_WINDOW
1010
1011 if (own_window.get(*state) && (fixed_pos == 0)) {
1012 int border_total = get_border_total();
1013 text_start_x = text_start_y = border_total;
1014 window.x = x - border_total;
1015 window.y = y - border_total;
1016 } else
1017 #endif
1018 {
1019 text_start_x = x;
1020 text_start_y = y;
1021 }
1022 /* update lua window globals */
1023 llua_update_window_table(text_start_x, text_start_y, text_width, text_height);
1024 }
1025
1026 /* drawing stuff */
1027
1028 static int cur_x, cur_y; /* current x and y for drawing */
1029 #endif
1030 // draw_mode also without BUILD_X11 because we only need to print to stdout with
1031 // FG
1032 static int draw_mode; /* FG, BG or OUTLINE */
1033 #ifdef BUILD_X11
1034 static long current_color;
1035
1036 static int saved_coordinates_x[100];
1037 static int saved_coordinates_y[100];
1038
get_saved_coordinates_x(int i)1039 int get_saved_coordinates_x(int i) { return saved_coordinates_x[i]; }
get_saved_coordinates_y(int i)1040 int get_saved_coordinates_y(int i) { return saved_coordinates_y[i]; }
1041
text_size_updater(char * s,int special_index)1042 static int text_size_updater(char *s, int special_index) {
1043 int w = 0;
1044 char *p;
1045 special_t *current = specials;
1046
1047 for (int i = 0; i < special_index; i++) { current = current->next; }
1048
1049 if (!out_to_x.get(*state)) { return 0; }
1050 /* get string widths and skip specials */
1051 p = s;
1052 while (*p != 0) {
1053 if (*p == SPECIAL_CHAR) {
1054 *p = '\0';
1055 w += get_string_width(s);
1056 *p = SPECIAL_CHAR;
1057
1058 if (current->type == BAR || current->type == GAUGE ||
1059 current->type == GRAPH) {
1060 w += current->width;
1061 if (current->height > last_font_height) {
1062 last_font_height = current->height;
1063 last_font_height += font_height();
1064 }
1065 } else if (current->type == OFFSET) {
1066 if (current->arg > 0) { w += current->arg; }
1067 } else if (current->type == VOFFSET) {
1068 last_font_height += current->arg;
1069 } else if (current->type == GOTO) {
1070 if (current->arg > cur_x) { w = static_cast<int>(current->arg); }
1071 } else if (current->type == TAB) {
1072 int start = current->arg;
1073 int step = current->width;
1074
1075 if ((step == 0) || step < 0) { step = 10; }
1076 w += step - (cur_x - text_start_x - start) % step;
1077 } else if (current->type == FONT) {
1078 selected_font = current->font_added;
1079 if (font_height() > last_font_height) {
1080 last_font_height = font_height();
1081 }
1082 }
1083
1084 special_index++;
1085 current = current->next;
1086 s = p + 1;
1087 }
1088 p++;
1089 }
1090
1091 w += get_string_width(s);
1092
1093 if (w > text_width) { text_width = w; }
1094 int mw = xft_dpi_scale(maximum_width.get(*state));
1095 if (text_width > mw && mw > 0) { text_width = mw; }
1096
1097 text_height += last_font_height;
1098 last_font_height = font_height();
1099 return special_index;
1100 }
1101 #endif /* BUILD_X11 */
1102
set_foreground_color(long c)1103 static inline void set_foreground_color(long c) {
1104 #ifdef BUILD_X11
1105 if (out_to_x.get(*state)) {
1106 #ifdef BUILD_ARGB
1107 if (have_argb_visual) {
1108 current_color = c | (own_window_argb_value.get(*state) << 24);
1109 } else {
1110 #endif /* BUILD_ARGB */
1111 current_color = c;
1112 #ifdef BUILD_ARGB
1113 }
1114 #endif /* BUILD_ARGB */
1115 XSetForeground(display, window.gc, current_color);
1116 }
1117 #endif /* BUILD_X11 */
1118 #ifdef BUILD_NCURSES
1119 if (out_to_ncurses.get(*state)) { attron(COLOR_PAIR(c)); }
1120 #endif /* BUILD_NCURSES */
1121 UNUSED(c);
1122 }
1123
string_replace_all(std::string original,const std::string & oldpart,const std::string & newpart,std::string::size_type start)1124 std::string string_replace_all(std::string original, const std::string &oldpart,
1125 const std::string &newpart,
1126 std::string::size_type start) {
1127 std::string::size_type i = start;
1128 int oldpartlen = oldpart.length();
1129 while (1) {
1130 i = original.find(oldpart, i);
1131 if (i == std::string::npos) { break; }
1132 original.replace(i, oldpartlen, newpart);
1133 }
1134 return original;
1135 }
1136
draw_string(const char * s)1137 static void draw_string(const char *s) {
1138 int i;
1139 int i2;
1140 int pos;
1141 #ifdef BUILD_X11
1142 int width_of_s;
1143 #endif /* BUILD_X11 */
1144 int max = 0;
1145 int added;
1146
1147 if (s[0] == '\0') { return; }
1148
1149 #ifdef BUILD_X11
1150 width_of_s = get_string_width(s);
1151 #endif /* BUILD_X11 */
1152 if (out_to_stdout.get(*state) && draw_mode == FG) {
1153 printf("%s\n", s);
1154 if (extra_newline.get(*state)) { fputc('\n', stdout); }
1155 fflush(stdout); /* output immediately, don't buffer */
1156 }
1157 if (out_to_stderr.get(*state) && draw_mode == FG) {
1158 fprintf(stderr, "%s\n", s);
1159 fflush(stderr); /* output immediately, don't buffer */
1160 }
1161 if (draw_mode == FG && (overwrite_fpointer != nullptr)) {
1162 fprintf(overwrite_fpointer, "%s\n", s);
1163 }
1164 if (draw_mode == FG && (append_fpointer != nullptr)) {
1165 fprintf(append_fpointer, "%s\n", s);
1166 }
1167 #ifdef BUILD_NCURSES
1168 if (out_to_ncurses.get(*state) && draw_mode == FG) { printw("%s", s); }
1169 #endif /* BUILD_NCURSES */
1170 #ifdef BUILD_HTTP
1171 if (out_to_http.get(*state) && draw_mode == FG) {
1172 std::string::size_type origlen = webpage.length();
1173 webpage.append(s);
1174 webpage = string_replace_all(webpage, "\n", "<br />", origlen);
1175 webpage = string_replace_all(webpage, " ", " ", origlen);
1176 webpage = string_replace_all(webpage, " ", " ", origlen);
1177 webpage.append("<br />");
1178 }
1179 #endif /* BUILD_HTTP */
1180 int tbs = text_buffer_size.get(*state);
1181 memset(tmpstring1, 0, tbs);
1182 memset(tmpstring2, 0, tbs);
1183 strncpy(tmpstring1, s, tbs - 1);
1184 pos = 0;
1185 added = 0;
1186
1187 #ifdef BUILD_X11
1188 if (out_to_x.get(*state)) {
1189 max = ((text_width - width_of_s) / get_string_width(" "));
1190 }
1191 #endif /* BUILD_X11 */
1192 /* This code looks for tabs in the text and coverts them to spaces.
1193 * The trick is getting the correct number of spaces, and not going
1194 * over the window's size without forcing the window larger. */
1195 for (i = 0; i < tbs; i++) {
1196 if (tmpstring1[i] == '\t') {
1197 i2 = 0;
1198 for (i2 = 0; i2 < (8 - (1 + pos) % 8) && added <= max; i2++) {
1199 /* guard against overrun */
1200 tmpstring2[std::min(pos + i2, tbs - 1)] = ' ';
1201 added++;
1202 }
1203 pos += i2;
1204 } else {
1205 /* guard against overrun */
1206 tmpstring2[std::min(pos, tbs - 1)] = tmpstring1[i];
1207 pos++;
1208 }
1209 }
1210 #ifdef BUILD_X11
1211 if (out_to_x.get(*state)) {
1212 int mw = xft_dpi_scale(maximum_width.get(*state));
1213 if (text_width == mw) {
1214 /* this means the text is probably pushing the limit,
1215 * so we'll chop it */
1216 while (cur_x + get_string_width(tmpstring2) - text_start_x > mw &&
1217 strlen(tmpstring2) > 0) {
1218 tmpstring2[strlen(tmpstring2) - 1] = '\0';
1219 }
1220 }
1221 }
1222 #endif /* BUILD_X11 */
1223 s = tmpstring2;
1224 #ifdef BUILD_X11
1225 if (out_to_x.get(*state)) {
1226 #ifdef BUILD_XFT
1227 if (use_xft.get(*state)) {
1228 XColor c;
1229 XftColor c2;
1230
1231 c.pixel = current_color;
1232 // query color on custom colormap
1233 XQueryColor(display, window.colourmap, &c);
1234
1235 c2.pixel = c.pixel;
1236 c2.color.red = c.red;
1237 c2.color.green = c.green;
1238 c2.color.blue = c.blue;
1239 c2.color.alpha = fonts[selected_font].font_alpha;
1240 if (utf8_mode.get(*state)) {
1241 XftDrawStringUtf8(window.xftdraw, &c2, fonts[selected_font].xftfont,
1242 text_offset_x + cur_x, text_offset_y + cur_y,
1243 reinterpret_cast<const XftChar8 *>(s), strlen(s));
1244 } else {
1245 XftDrawString8(window.xftdraw, &c2, fonts[selected_font].xftfont,
1246 text_offset_x + cur_x, text_offset_y + cur_y,
1247 reinterpret_cast<const XftChar8 *>(s), strlen(s));
1248 }
1249 } else
1250 #endif
1251 {
1252 if (utf8_mode.get(*state)) {
1253 Xutf8DrawString(display, window.drawable, fonts[selected_font].fontset,
1254 window.gc, text_offset_x + cur_x, text_offset_y + cur_y,
1255 s, strlen(s));
1256 } else {
1257 XDrawString(display, window.drawable, window.gc, text_offset_x + cur_x,
1258 text_offset_y + cur_y, s, strlen(s));
1259 }
1260 }
1261 cur_x += width_of_s;
1262 }
1263 #endif /* BUILD_X11 */
1264 memcpy(tmpstring1, s, tbs);
1265 }
1266
1267 #if defined(BUILD_MATH) && defined(BUILD_X11)
1268 /// Format \a size as a real followed by closest SI unit, with \a prec number
1269 /// of digits after the decimal point.
formatSizeWithUnits(double size,int prec=1)1270 static std::string formatSizeWithUnits(double size, int prec = 1) {
1271 int div = 0;
1272 double rem = 0;
1273
1274 while (size >= 1024.0 &&
1275 static_cast<size_t>(div) < (sizeof suffixes / sizeof *suffixes)) {
1276 rem = fmod(size, 1024.0);
1277 div++;
1278 size /= 1024.0;
1279 }
1280
1281 double size_d = size + rem / 1024.0;
1282
1283 std::ostringstream result;
1284 result.setf(std::ios::fixed, std::ios::floatfield);
1285 result.precision(prec);
1286 result << size_d;
1287 result << " ";
1288
1289 if (short_units.get(*state)) {
1290 result << suffixes[div][0];
1291 } else {
1292 result << suffixes[div];
1293 }
1294
1295 return result.str();
1296 }
1297 #endif /* BUILD_MATH && BUILD_X11 */
1298
draw_each_line_inner(char * s,int special_index,int last_special_applied)1299 int draw_each_line_inner(char *s, int special_index, int last_special_applied) {
1300 #ifndef BUILD_X11
1301 static int cur_x, cur_y; /* current x and y for drawing */
1302 (void)cur_y;
1303 #endif
1304 #ifdef BUILD_X11
1305 int font_h = 0;
1306 int cur_y_add = 0;
1307 int mw = xft_dpi_scale(maximum_width.get(*state));
1308 #endif /* BUILD_X11 */
1309 char *p = s;
1310 int orig_special_index = special_index;
1311
1312 #ifdef BUILD_X11
1313 if (out_to_x.get(*state)) {
1314 font_h = font_height();
1315 cur_y += font_ascent();
1316 }
1317 cur_x = text_start_x;
1318 #endif /* BUILD_X11 */
1319
1320 while (*p != 0) {
1321 if (*p == SPECIAL_CHAR || last_special_applied > -1) {
1322 #ifdef BUILD_X11
1323 int w = 0;
1324 #endif /* BUILD_X11 */
1325
1326 /* draw string before special, unless we're dealing multiline
1327 * specials */
1328 if (last_special_applied > -1) {
1329 special_index = last_special_applied;
1330 } else {
1331 *p = '\0';
1332 draw_string(s);
1333 *p = SPECIAL_CHAR;
1334 s = p + 1;
1335 }
1336 /* draw special */
1337 special_t *current = specials;
1338 for (int i = 0; i < special_index; i++) { current = current->next; }
1339 switch (current->type) {
1340 #ifdef BUILD_X11
1341 case HORIZONTAL_LINE:
1342 if (out_to_x.get(*state)) {
1343 int h = current->height;
1344 int mid = font_ascent() / 2;
1345
1346 w = text_start_x + text_width - cur_x;
1347
1348 XSetLineAttributes(display, window.gc, h, LineSolid, CapButt,
1349 JoinMiter);
1350 XDrawLine(display, window.drawable, window.gc,
1351 text_offset_x + cur_x, text_offset_y + cur_y - mid / 2,
1352 text_offset_x + cur_x + w,
1353 text_offset_y + cur_y - mid / 2);
1354 }
1355 break;
1356
1357 case STIPPLED_HR:
1358 if (out_to_x.get(*state)) {
1359 int h = current->height;
1360 char tmp_s = current->arg;
1361 int mid = font_ascent() / 2;
1362 char ss[2] = {tmp_s, tmp_s};
1363
1364 w = text_start_x + text_width - cur_x - 1;
1365 XSetLineAttributes(display, window.gc, h, LineOnOffDash, CapButt,
1366 JoinMiter);
1367 XSetDashes(display, window.gc, 0, ss, 2);
1368 XDrawLine(display, window.drawable, window.gc,
1369 text_offset_x + cur_x, text_offset_y + cur_y - mid / 2,
1370 text_offset_x + cur_x + w,
1371 text_offset_x + cur_y - mid / 2);
1372 }
1373 break;
1374
1375 case BAR:
1376 if (out_to_x.get(*state)) {
1377 int h, by;
1378 double bar_usage, scale;
1379 if (cur_x - text_start_x > mw && mw > 0) { break; }
1380 h = current->height;
1381 bar_usage = current->arg;
1382 scale = current->scale;
1383 by = cur_y - (font_ascent() / 2) - 1;
1384
1385 if (h < font_h) { by -= h / 2 - 1; }
1386 w = current->width;
1387 if (w == 0) { w = text_start_x + text_width - cur_x - 1; }
1388 if (w < 0) { w = 0; }
1389
1390 XSetLineAttributes(display, window.gc, xft_dpi_scale(1), LineSolid,
1391 CapButt, JoinMiter);
1392
1393 XDrawRectangle(display, window.drawable, window.gc,
1394 text_offset_x + cur_x, text_offset_y + by, w, h);
1395 XFillRectangle(display, window.drawable, window.gc,
1396 text_offset_x + cur_x, text_offset_y + by,
1397 w * bar_usage / scale, h);
1398 if (h > cur_y_add && h > font_h) { cur_y_add = h; }
1399 }
1400 break;
1401
1402 case GAUGE: /* new GAUGE */
1403 if (out_to_x.get(*state)) {
1404 int h, by = 0;
1405 unsigned long last_colour = current_color;
1406 #ifdef BUILD_MATH
1407 float angle, px, py;
1408 double usage, scale;
1409 #endif /* BUILD_MATH */
1410
1411 if (cur_x - text_start_x > mw && mw > 0) { break; }
1412
1413 h = current->height;
1414 by = cur_y - (font_ascent() / 2) - 1;
1415
1416 if (h < font_h) { by -= h / 2 - 1; }
1417 w = current->width;
1418 if (w == 0) { w = text_start_x + text_width - cur_x - 1; }
1419 if (w < 0) { w = 0; }
1420
1421 XSetLineAttributes(display, window.gc, 1, LineSolid, CapButt,
1422 JoinMiter);
1423
1424 XDrawArc(display, window.drawable, window.gc, text_offset_x + cur_x,
1425 text_offset_y + by, w, h * 2, 0, 180 * 64);
1426
1427 #ifdef BUILD_MATH
1428 usage = current->arg;
1429 scale = current->scale;
1430 angle = M_PI * usage / scale;
1431 px = static_cast<float>(cur_x + (w / 2.)) -
1432 static_cast<float>(w / 2.) * cos(angle);
1433 py = static_cast<float>(by + (h)) -
1434 static_cast<float>(h) * sin(angle);
1435
1436 XDrawLine(display, window.drawable, window.gc,
1437 text_offset_x + cur_x + (w / 2.),
1438 text_offset_y + by + (h),
1439 text_offset_x + static_cast<int>(px),
1440 text_offset_y + static_cast<int>(py));
1441 #endif /* BUILD_MATH */
1442
1443 if (h > cur_y_add && h > font_h) { cur_y_add = h; }
1444
1445 set_foreground_color(last_colour);
1446 }
1447 break;
1448
1449 case GRAPH:
1450 if (out_to_x.get(*state)) {
1451 int h, by, i = 0, j = 0;
1452 int colour_idx = 0;
1453 unsigned long last_colour = current_color;
1454 if (cur_x - text_start_x > mw && mw > 0) { break; }
1455 h = current->height;
1456 by = cur_y - (font_ascent() / 2) - 1;
1457
1458 if (h < font_h) { by -= h / 2 - 1; }
1459 w = current->width;
1460 if (w == 0) {
1461 w = text_start_x + text_width - cur_x - 1;
1462 current->graph_width = std::max(w - 1, 0);
1463 if (current->graph_width != current->graph_allocated) {
1464 w = current->graph_allocated + 1;
1465 }
1466 }
1467 if (w < 0) { w = 0; }
1468 if (draw_graph_borders.get(*state)) {
1469 XSetLineAttributes(display, window.gc, xft_dpi_scale(1),
1470 LineSolid, CapButt, JoinMiter);
1471 XDrawRectangle(display, window.drawable, window.gc,
1472 text_offset_x + cur_x, text_offset_y + by, w, h);
1473 }
1474 XSetLineAttributes(display, window.gc, 1, LineSolid, CapButt,
1475 JoinMiter);
1476
1477 /* in case we don't have a graph yet */
1478 if (current->graph != nullptr) {
1479 std::unique_ptr<unsigned long[]> tmpcolour;
1480
1481 if (current->last_colour != 0 || current->first_colour != 0) {
1482 #ifdef BUILD_HSV_GRADIENT
1483 tmpcolour = do_hsv_gradient(w - 1, current->last_colour,
1484 current->first_colour);
1485 #else /* BUILD_HSV_GRADIENT */
1486 tmpcolour = do_gradient(w - 1, current->last_colour,
1487 current->first_colour);
1488 #endif /* BUILD_HSV_GRADIENT */
1489 }
1490 colour_idx = 0;
1491 for (i = w - 2; i > -1; i--) {
1492 if (current->last_colour != 0 || current->first_colour != 0) {
1493 if (current->tempgrad != 0) {
1494 set_foreground_color(tmpcolour[static_cast<int>(
1495 static_cast<float>(w - 2) -
1496 current->graph[j] * (w - 2) /
1497 std::max(static_cast<float>(current->scale),
1498 1.0F))]);
1499 } else {
1500 set_foreground_color(tmpcolour[colour_idx++]);
1501 }
1502 }
1503 /* this is mugfugly, but it works */
1504 XDrawLine(display, window.drawable, window.gc,
1505 text_offset_x + cur_x + i + 1, text_offset_y + by + h,
1506 text_offset_x + cur_x + i + 1,
1507 text_offset_y + round_to_positive_int(
1508 static_cast<double>(by) + h -
1509 current->graph[j] * (h - 1) /
1510 current->scale));
1511 ++j;
1512 }
1513 }
1514 if (h > cur_y_add && h > font_h) { cur_y_add = h; }
1515 if (show_graph_range.get(*state)) {
1516 int tmp_x = cur_x;
1517 int tmp_y = cur_y;
1518 unsigned short int seconds = active_update_interval() * w;
1519 char *tmp_day_str;
1520 char *tmp_hour_str;
1521 char *tmp_min_str;
1522 char *tmp_sec_str;
1523 char *tmp_str;
1524 unsigned short int timeunits;
1525 if (seconds != 0) {
1526 timeunits = seconds / 86400;
1527 seconds %= 86400;
1528 if (timeunits <= 0 ||
1529 asprintf(&tmp_day_str, _("%dd"), timeunits) == -1) {
1530 tmp_day_str = strdup("");
1531 }
1532 timeunits = seconds / 3600;
1533 seconds %= 3600;
1534 if (timeunits <= 0 ||
1535 asprintf(&tmp_hour_str, _("%dh"), timeunits) == -1) {
1536 tmp_hour_str = strdup("");
1537 }
1538 timeunits = seconds / 60;
1539 seconds %= 60;
1540 if (timeunits <= 0 ||
1541 asprintf(&tmp_min_str, _("%dm"), timeunits) == -1) {
1542 tmp_min_str = strdup("");
1543 }
1544 if (seconds <= 0 ||
1545 asprintf(&tmp_sec_str, _("%ds"), seconds) == -1) {
1546 tmp_sec_str = strdup("");
1547 }
1548 if (asprintf(&tmp_str, "%s%s%s%s", tmp_day_str, tmp_hour_str,
1549 tmp_min_str, tmp_sec_str) == -1) {
1550 tmp_str = strdup("");
1551 }
1552 free(tmp_day_str);
1553 free(tmp_hour_str);
1554 free(tmp_min_str);
1555 free(tmp_sec_str);
1556 } else {
1557 tmp_str = strdup(
1558 _("Range not possible")); // should never happen, but
1559 // better safe then sorry
1560 }
1561 cur_x += (w / 2) - (font_ascent() * (strlen(tmp_str) / 2));
1562 cur_y += font_h / 2;
1563 draw_string(tmp_str);
1564 free(tmp_str);
1565 cur_x = tmp_x;
1566 cur_y = tmp_y;
1567 }
1568 #ifdef BUILD_MATH
1569 if (show_graph_scale.get(*state) && (current->show_scale == 1)) {
1570 int tmp_x = cur_x;
1571 int tmp_y = cur_y;
1572 cur_x += font_ascent() / 2;
1573 cur_y += font_h / 2;
1574 std::string tmp_str = formatSizeWithUnits(
1575 current->scale_log != 0 ? std::pow(10.0, current->scale)
1576 : current->scale);
1577 draw_string(tmp_str.c_str());
1578 cur_x = tmp_x;
1579 cur_y = tmp_y;
1580 }
1581 #endif
1582 set_foreground_color(last_colour);
1583 }
1584 break;
1585
1586 case FONT:
1587 if (out_to_x.get(*state)) {
1588 int old = font_ascent();
1589
1590 cur_y -= font_ascent();
1591 selected_font = current->font_added;
1592 set_font();
1593 if (cur_y + font_ascent() < cur_y + old) {
1594 cur_y += old;
1595 } else {
1596 cur_y += font_ascent();
1597 }
1598 font_h = font_height();
1599 }
1600 break;
1601 #endif /* BUILD_X11 */
1602 case FG:
1603 if (draw_mode == FG) { set_foreground_color(current->arg); }
1604 break;
1605
1606 #ifdef BUILD_X11
1607 case BG:
1608 if (draw_mode == BG) { set_foreground_color(current->arg); }
1609 break;
1610
1611 case OUTLINE:
1612 if (draw_mode == OUTLINE) { set_foreground_color(current->arg); }
1613 break;
1614
1615 case OFFSET:
1616 w += current->arg;
1617 break;
1618
1619 case VOFFSET:
1620 cur_y += current->arg;
1621 break;
1622
1623 case SAVE_COORDINATES:
1624 saved_coordinates_x[static_cast<int>(current->arg)] =
1625 cur_x - text_start_x;
1626 saved_coordinates_y[static_cast<int>(current->arg)] =
1627 cur_y - text_start_y - last_font_height;
1628 break;
1629
1630 case TAB: {
1631 int start = current->arg;
1632 int step = current->width;
1633
1634 if ((step == 0) || step < 0) { step = 10; }
1635 w = step - (cur_x - text_start_x - start) % step;
1636 break;
1637 }
1638
1639 case ALIGNR: {
1640 /* TODO: add back in "+ window.border_inner_margin" to the end of
1641 * this line? */
1642 int pos_x = text_start_x + text_width -
1643 get_string_width_special(s, special_index);
1644
1645 /* printf("pos_x %i text_start_x %i text_width %i cur_x %i "
1646 "get_string_width(p) %i gap_x %i "
1647 "current->arg %i window.border_inner_margin %i "
1648 "window.border_width %i\n", pos_x, text_start_x, text_width,
1649 cur_x, get_string_width_special(s), gap_x,
1650 current->arg, window.border_inner_margin,
1651 window.border_width); */
1652 if (pos_x > current->arg && pos_x > cur_x) {
1653 cur_x = pos_x - current->arg;
1654 }
1655 break;
1656 }
1657
1658 case ALIGNC: {
1659 int pos_x = (text_width) / 2 -
1660 get_string_width_special(s, special_index) / 2 -
1661 (cur_x - text_start_x);
1662 /* int pos_x = text_start_x + text_width / 2 -
1663 get_string_width_special(s) / 2; */
1664
1665 /* printf("pos_x %i text_start_x %i text_width %i cur_x %i "
1666 "get_string_width(p) %i gap_x %i "
1667 "current->arg %i\n", pos_x, text_start_x,
1668 text_width, cur_x, get_string_width(s), gap_x,
1669 current->arg); */
1670 if (pos_x > current->arg) { w = pos_x - current->arg; }
1671 break;
1672 }
1673 #endif /* BUILD_X11 */
1674 case GOTO:
1675 if (current->arg >= 0) {
1676 #ifdef BUILD_X11
1677 cur_x = static_cast<int>(current->arg);
1678 // make sure shades are 1 pixel to the right of the text
1679 if (draw_mode == BG) { cur_x++; }
1680 #endif /* BUILD_X11 */
1681 #ifdef BUILD_NCURSES
1682 cur_x = static_cast<int>(current->arg);
1683 if (out_to_ncurses.get(*state)) {
1684 int x, y;
1685 getyx(ncurses_window, y, x);
1686 move(y, cur_x);
1687 }
1688 #endif /* BUILD_NCURSES */
1689 }
1690 break;
1691 }
1692
1693 #ifdef BUILD_X11
1694 cur_x += w;
1695 #endif /* BUILD_X11 */
1696
1697 if (special_index != last_special_applied) {
1698 special_index++;
1699 } else {
1700 special_index = orig_special_index;
1701 last_special_applied = -1;
1702 }
1703 }
1704 p++;
1705 }
1706
1707 #ifdef BUILD_X11
1708 cur_y += cur_y_add;
1709 #endif /* BUILD_X11 */
1710 draw_string(s);
1711 #ifdef BUILD_NCURSES
1712 if (out_to_ncurses.get(*state)) { printw("\n"); }
1713 #endif /* BUILD_NCURSES */
1714 #ifdef BUILD_X11
1715 if (out_to_x.get(*state)) { cur_y += font_descent(); }
1716 #endif /* BUILD_X11 */
1717 return special_index;
1718 }
1719
draw_line(char * s,int special_index)1720 static int draw_line(char *s, int special_index) {
1721 #ifdef BUILD_X11
1722 if (out_to_x.get(*state)) {
1723 return draw_each_line_inner(s, special_index, -1);
1724 }
1725 #endif /* BUILD_X11 */
1726 #ifdef BUILD_NCURSES
1727 if (out_to_ncurses.get(*state)) {
1728 return draw_each_line_inner(s, special_index, -1);
1729 }
1730 #endif /* BUILD_NCURSES */
1731 draw_string(s);
1732 UNUSED(special_index);
1733 return 0;
1734 }
1735
draw_text()1736 static void draw_text() {
1737 #ifdef BUILD_HTTP
1738 #define WEBPAGE_START1 \
1739 "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" " \
1740 "\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n<html " \
1741 "xmlns=\"http://www.w3.org/1999/xhtml\"><head><meta " \
1742 "http-equiv=\"Content-type\" content=\"text/html;charset=UTF-8\" />"
1743 #define WEBPAGE_START2 \
1744 "<title>Conky</title></head><body style=\"font-family: monospace\"><p>"
1745 #define WEBPAGE_END "</p></body></html>"
1746 if (out_to_http.get(*state)) {
1747 webpage = WEBPAGE_START1;
1748 if (http_refresh.get(*state)) {
1749 webpage.append("<meta http-equiv=\"refresh\" content=\"");
1750 std::stringstream update_interval_str;
1751 update_interval_str << update_interval.get(*state);
1752 webpage.append(update_interval_str.str());
1753 webpage.append("\" />");
1754 }
1755 webpage.append(WEBPAGE_START2);
1756 }
1757 #endif /* BUILD_HTTP */
1758 #ifdef BUILD_X11
1759 if (out_to_x.get(*state)) {
1760 cur_y = text_start_y;
1761 int bw = xft_dpi_scale(border_width.get(*state));
1762
1763 /* draw borders */
1764 if (draw_borders.get(*state) && bw > 0) {
1765 if (stippled_borders.get(*state) != 0) {
1766 char ss[2] = {(char)xft_dpi_scale(stippled_borders.get(*state)),
1767 (char)xft_dpi_scale(stippled_borders.get(*state))};
1768 XSetLineAttributes(display, window.gc, bw, LineOnOffDash, CapButt,
1769 JoinMiter);
1770 XSetDashes(display, window.gc, 0, ss, 2);
1771 } else {
1772 XSetLineAttributes(display, window.gc, bw, LineSolid, CapButt,
1773 JoinMiter);
1774 }
1775
1776 int offset = xft_dpi_scale(border_inner_margin.get(*state)) + bw;
1777 XDrawRectangle(display, window.drawable, window.gc,
1778 text_offset_x + text_start_x - offset,
1779 text_offset_y + text_start_y - offset,
1780 text_width + 2 * offset, text_height + 2 * offset);
1781 }
1782
1783 /* draw text */
1784 }
1785 setup_fonts();
1786 #endif /* BUILD_X11 */
1787 #ifdef BUILD_NCURSES
1788 init_pair(COLOR_WHITE, COLOR_WHITE, COLOR_BLACK);
1789 attron(COLOR_PAIR(COLOR_WHITE));
1790 #endif /* BUILD_NCURSES */
1791 for_each_line(text_buffer, draw_line);
1792 #ifdef BUILD_HTTP
1793 if (out_to_http.get(*state)) { webpage.append(WEBPAGE_END); }
1794 #endif /* BUILD_HTTP */
1795 }
1796
draw_stuff()1797 static void draw_stuff() {
1798 #ifdef BUILD_X11
1799 text_offset_x = text_offset_y = 0;
1800 #ifdef BUILD_IMLIB2
1801 cimlib_render(text_start_x, text_start_y, window.width, window.height);
1802 #endif /* BUILD_IMLIB2 */
1803 #endif /* BUILD_X11 */
1804 if (static_cast<unsigned int>(!overwrite_file.get(*state).empty()) != 0u) {
1805 overwrite_fpointer = fopen(overwrite_file.get(*state).c_str(), "we");
1806 if (overwrite_fpointer == nullptr) {
1807 NORM_ERR("Cannot overwrite '%s'", overwrite_file.get(*state).c_str());
1808 }
1809 }
1810 if (static_cast<unsigned int>(!append_file.get(*state).empty()) != 0u) {
1811 append_fpointer = fopen(append_file.get(*state).c_str(), "ae");
1812 if (append_fpointer == nullptr) {
1813 NORM_ERR("Cannot append to '%s'", append_file.get(*state).c_str());
1814 }
1815 }
1816 #ifdef BUILD_X11
1817 llua_draw_pre_hook();
1818 if (out_to_x.get(*state)) {
1819 selected_font = 0;
1820 if (draw_shades.get(*state) && !draw_outline.get(*state)) {
1821 text_offset_x = text_offset_y = 1;
1822 set_foreground_color(default_shade_color.get(*state));
1823 draw_mode = BG;
1824 draw_text();
1825 text_offset_x = text_offset_y = 0;
1826 }
1827
1828 if (draw_outline.get(*state)) {
1829 selected_font = 0;
1830
1831 for (text_offset_x = -1; text_offset_x < 2; text_offset_x++) {
1832 for (text_offset_y = -1; text_offset_y < 2; text_offset_y++) {
1833 if (text_offset_x == 0 && text_offset_y == 0) { continue; }
1834 set_foreground_color(default_outline_color.get(*state));
1835 draw_mode = OUTLINE;
1836 draw_text();
1837 }
1838 }
1839 text_offset_x = text_offset_y = 0;
1840 }
1841
1842 set_foreground_color(default_color.get(*state));
1843 }
1844 #endif /* BUILD_X11 */
1845 draw_mode = FG;
1846 draw_text();
1847 #if defined(BUILD_X11)
1848 llua_draw_post_hook();
1849 #if defined(BUILD_XDBE)
1850 if (out_to_x.get(*state)) { xdbe_swap_buffers(); }
1851 #else
1852 if (out_to_x.get(*state)) { xpmdb_swap_buffers(); }
1853 #endif
1854 #endif /* BUILD_X11 && BUILD_XDBE */
1855 if (overwrite_fpointer != nullptr) {
1856 fclose(overwrite_fpointer);
1857 overwrite_fpointer = nullptr;
1858 }
1859 if (append_fpointer != nullptr) {
1860 fclose(append_fpointer);
1861 append_fpointer = nullptr;
1862 }
1863 }
1864
1865 #ifdef BUILD_X11
clear_text(int exposures)1866 static void clear_text(int exposures) {
1867 #ifdef BUILD_XDBE
1868 if (use_xdbe.get(*state)) {
1869 /* The swap action is XdbeBackground, which clears */
1870 return;
1871 }
1872 #else
1873 if (use_xpmdb.get(*state)) {
1874 return;
1875 } else
1876 #endif
1877 if ((display != nullptr) &&
1878 (window.window != 0u)) { // make sure these are !null
1879 /* there is some extra space for borders and outlines */
1880 int border_total = get_border_total();
1881
1882 XClearArea(display, window.window, text_start_x - border_total,
1883 text_start_y - border_total, text_width + 2 * border_total,
1884 text_height + 2 * border_total, exposures != 0 ? True : 0);
1885 }
1886 }
1887 #endif /* BUILD_X11 */
1888
1889 static int need_to_update;
1890
1891 /* update_text() generates new text and clears old text area */
update_text()1892 static void update_text() {
1893 #ifdef BUILD_IMLIB2
1894 cimlib_cleanup();
1895 #endif /* BUILD_IMLIB2 */
1896 generate_text();
1897 #ifdef BUILD_X11
1898 if (out_to_x.get(*state)) { clear_text(1); }
1899 #endif /* BUILD_X11 */
1900 need_to_update = 1;
1901 llua_update_info(&info, active_update_interval());
1902 }
1903
1904 #ifdef HAVE_SYS_INOTIFY_H
1905 int inotify_fd = -1;
1906 #endif
1907
1908 template <typename Out>
split(const std::string & s,char delim,Out result)1909 void split(const std::string &s, char delim, Out result) {
1910 std::stringstream ss(s);
1911 std::string item;
1912 while (std::getline(ss, item, delim)) { *(result++) = item; }
1913 }
split(const std::string & s,char delim)1914 std::vector<std::string> split(const std::string &s, char delim) {
1915 std::vector<std::string> elems;
1916 split(s, delim, std::back_inserter(elems));
1917 return elems;
1918 }
1919
is_on_battery()1920 bool is_on_battery() { // checks if at least one battery specified in
1921 // "detect_battery" is discharging
1922 char buf[64];
1923 std::vector<std::string> b_items = split(detect_battery.get(*state), ',');
1924
1925 for (auto const &value : b_items) {
1926 get_battery_short_status(buf, 64, value.c_str());
1927 if (buf[0] == 'D') { return true; }
1928 }
1929 return false;
1930 }
1931
1932 volatile sig_atomic_t g_sigterm_pending, g_sighup_pending, g_sigusr2_pending;
1933
main_loop()1934 void main_loop() {
1935 int terminate = 0;
1936 #ifdef SIGNAL_BLOCKING
1937 sigset_t newmask, oldmask;
1938 #endif
1939 #ifdef BUILD_X11
1940 double t;
1941 #endif /* BUILD_X11 */
1942 #ifdef HAVE_SYS_INOTIFY_H
1943 int inotify_config_wd = -1;
1944 #define INOTIFY_EVENT_SIZE (sizeof(struct inotify_event))
1945 #define INOTIFY_BUF_LEN (20 * (INOTIFY_EVENT_SIZE + 16)) + 1
1946 char inotify_buff[INOTIFY_BUF_LEN];
1947 #endif /* HAVE_SYS_INOTIFY_H */
1948
1949 #ifdef SIGNAL_BLOCKING
1950 sigemptyset(&newmask);
1951 sigaddset(&newmask, SIGINT);
1952 sigaddset(&newmask, SIGTERM);
1953 sigaddset(&newmask, SIGUSR1);
1954 #endif
1955
1956 last_update_time = 0.0;
1957 next_update_time = get_time() - fmod(get_time(), active_update_interval());
1958 info.looped = 0;
1959 while (terminate == 0 && (total_run_times.get(*state) == 0 ||
1960 info.looped < total_run_times.get(*state))) {
1961 if ((update_interval_on_battery.get(*state) != NOBATTERY)) {
1962 on_battery = is_on_battery();
1963 }
1964 info.looped++;
1965
1966 #ifdef SIGNAL_BLOCKING
1967 /* block signals. we will inspect for pending signals later */
1968 if (sigprocmask(SIG_BLOCK, &newmask, &oldmask) < 0) {
1969 CRIT_ERR(nullptr, NULL, "unable to sigprocmask()");
1970 }
1971 #endif
1972
1973 #ifdef BUILD_X11
1974 if (out_to_x.get(*state)) {
1975 /* wait for X event or timeout */
1976
1977 if (XPending(display) == 0) {
1978 fd_set fdsr;
1979 struct timeval tv {};
1980 int s;
1981 t = next_update_time - get_time();
1982
1983 t = std::min(std::max(t, 0.0), active_update_interval());
1984
1985 tv.tv_sec = static_cast<long>(t);
1986 tv.tv_usec = static_cast<long>(t * 1000000) % 1000000;
1987 FD_ZERO(&fdsr);
1988 FD_SET(ConnectionNumber(display), &fdsr);
1989
1990 s = select(ConnectionNumber(display) + 1, &fdsr, nullptr, nullptr, &tv);
1991 if (s == -1) {
1992 if (errno != EINTR) {
1993 NORM_ERR("can't select(): %s", strerror(errno));
1994 }
1995 } else {
1996 /* timeout */
1997 if (s == 0) { update_text(); }
1998 }
1999 }
2000
2001 if (need_to_update != 0) {
2002 #ifdef OWN_WINDOW
2003 int wx = window.x, wy = window.y;
2004 #endif
2005
2006 need_to_update = 0;
2007 selected_font = 0;
2008 update_text_area();
2009
2010 #ifdef OWN_WINDOW
2011 if (own_window.get(*state)) {
2012 int changed = 0;
2013 int border_total = get_border_total();
2014
2015 /* resize window if it isn't right size */
2016 if ((fixed_size == 0) &&
2017 (text_width + 2 * border_total != window.width ||
2018 text_height + 2 * border_total != window.height)) {
2019 window.width = text_width + 2 * border_total;
2020 window.height = text_height + 2 * border_total;
2021 draw_stuff(); /* redraw everything in our newly sized window */
2022 XResizeWindow(display, window.window, window.width,
2023 window.height); /* resize window */
2024 set_transparent_background(window.window);
2025 #ifdef BUILD_XDBE
2026 /* swap buffers */
2027 xdbe_swap_buffers();
2028 #else
2029 if (use_xpmdb.get(*state)) {
2030 XFreePixmap(display, window.back_buffer);
2031 window.back_buffer =
2032 XCreatePixmap(display, window.window, window.width,
2033 window.height, DefaultDepth(display, screen));
2034
2035 if (window.back_buffer != None) {
2036 window.drawable = window.back_buffer;
2037 } else {
2038 // this is probably reallllly bad
2039 NORM_ERR("Failed to allocate back buffer");
2040 }
2041 XSetForeground(display, window.gc, 0);
2042 XFillRectangle(display, window.drawable, window.gc, 0, 0,
2043 window.width, window.height);
2044 }
2045 #endif
2046
2047 changed++;
2048 /* update lua window globals */
2049 llua_update_window_table(text_start_x, text_start_y, text_width,
2050 text_height);
2051 }
2052
2053 /* move window if it isn't in right position */
2054 if ((fixed_pos == 0) && (window.x != wx || window.y != wy)) {
2055 XMoveWindow(display, window.window, window.x, window.y);
2056 changed++;
2057 }
2058
2059 /* update struts */
2060 if ((changed != 0) && own_window_type.get(*state) == TYPE_PANEL) {
2061 int sidenum = -1;
2062
2063 DBGP("%s", _(PACKAGE_NAME ": defining struts\n"));
2064 fflush(stderr);
2065
2066 switch (text_alignment.get(*state)) {
2067 case TOP_LEFT:
2068 case TOP_RIGHT:
2069 case TOP_MIDDLE: {
2070 sidenum = 2;
2071 break;
2072 }
2073 case BOTTOM_LEFT:
2074 case BOTTOM_RIGHT:
2075 case BOTTOM_MIDDLE: {
2076 sidenum = 3;
2077 break;
2078 }
2079 case MIDDLE_LEFT: {
2080 sidenum = 0;
2081 break;
2082 }
2083 case MIDDLE_RIGHT: {
2084 sidenum = 1;
2085 break;
2086 }
2087
2088 case NONE:
2089 case MIDDLE_MIDDLE: /* XXX What about these? */;
2090 }
2091
2092 set_struts(sidenum);
2093 }
2094 }
2095 #endif
2096
2097 clear_text(1);
2098
2099 #if defined(BUILD_XDBE)
2100 if (use_xdbe.get(*state)) {
2101 #else
2102 if (use_xpmdb.get(*state)) {
2103 #endif
2104 XRectangle r;
2105 int border_total = get_border_total();
2106
2107 r.x = text_start_x - border_total;
2108 r.y = text_start_y - border_total;
2109 r.width = text_width + 2 * border_total;
2110 r.height = text_height + 2 * border_total;
2111 XUnionRectWithRegion(&r, x11_stuff.region, x11_stuff.region);
2112 }
2113 }
2114
2115 /* handle X events */
2116 while (XPending(display) != 0) {
2117 XEvent ev;
2118
2119 XNextEvent(display, &ev);
2120 switch (ev.type) {
2121 case Expose: {
2122 XRectangle r;
2123 r.x = ev.xexpose.x;
2124 r.y = ev.xexpose.y;
2125 r.width = ev.xexpose.width;
2126 r.height = ev.xexpose.height;
2127 XUnionRectWithRegion(&r, x11_stuff.region, x11_stuff.region);
2128 XSync(display, False);
2129 break;
2130 }
2131
2132 case PropertyNotify: {
2133 if (ev.xproperty.state == PropertyNewValue) {
2134 get_x11_desktop_info(ev.xproperty.display, ev.xproperty.atom);
2135 }
2136 #ifdef USE_ARGB
2137 if (!have_argb_visual) {
2138 #endif
2139 if (ev.xproperty.atom == ATOM(_XROOTPMAP_ID) ||
2140 ev.xproperty.atom == ATOM(_XROOTMAP_ID)) {
2141 if (forced_redraw.get(*state)) {
2142 draw_stuff();
2143 next_update_time = get_time();
2144 need_to_update = 1;
2145 }
2146 }
2147 #ifdef USE_ARGB
2148 }
2149 #endif
2150 break;
2151 }
2152
2153 #ifdef OWN_WINDOW
2154 case ReparentNotify:
2155 /* make background transparent */
2156 if (own_window.get(*state)) {
2157 set_transparent_background(window.window);
2158 }
2159 break;
2160
2161 case ConfigureNotify:
2162 if (own_window.get(*state)) {
2163 /* if window size isn't what expected, set fixed size */
2164 if (ev.xconfigure.width != window.width ||
2165 ev.xconfigure.height != window.height) {
2166 if (window.width != 0 && window.height != 0) { fixed_size = 1; }
2167
2168 /* clear old stuff before screwing up
2169 * size and pos */
2170 clear_text(1);
2171
2172 {
2173 XWindowAttributes attrs;
2174 if (XGetWindowAttributes(display, window.window, &attrs) !=
2175 0) {
2176 window.width = attrs.width;
2177 window.height = attrs.height;
2178 }
2179 }
2180
2181 int border_total = get_border_total();
2182
2183 text_width = window.width - 2 * border_total;
2184 text_height = window.height - 2 * border_total;
2185 int mw = xft_dpi_scale(maximum_width.get(*state));
2186 if (text_width > mw && mw > 0) { text_width = mw; }
2187 }
2188
2189 /* if position isn't what expected, set fixed pos
2190 * total_updates avoids setting fixed_pos when window
2191 * is set to weird locations when started */
2192 /* // this is broken
2193 if (total_updates >= 2 && !fixed_pos
2194 && (window.x != ev.xconfigure.x
2195 || window.y != ev.xconfigure.y)
2196 && (ev.xconfigure.x != 0
2197 || ev.xconfigure.y != 0)) {
2198 fixed_pos = 1;
2199 } */
2200 }
2201 break;
2202
2203 case ButtonPress:
2204 if (own_window.get(*state)) {
2205 /* if an ordinary window with decorations */
2206 if ((own_window_type.get(*state) == TYPE_NORMAL &&
2207 !TEST_HINT(own_window_hints.get(*state),
2208 HINT_UNDECORATED)) ||
2209 own_window_type.get(*state) == TYPE_DESKTOP) {
2210 /* allow conky to hold input focus. */
2211 break;
2212 }
2213 /* forward the click to the desktop window */
2214 XUngrabPointer(display, ev.xbutton.time);
2215 ev.xbutton.window = window.desktop;
2216 ev.xbutton.x = ev.xbutton.x_root;
2217 ev.xbutton.y = ev.xbutton.y_root;
2218 XSendEvent(display, ev.xbutton.window, False, ButtonPressMask,
2219 &ev);
2220 XSetInputFocus(display, ev.xbutton.window, RevertToParent,
2221 ev.xbutton.time);
2222 }
2223 break;
2224
2225 case ButtonRelease:
2226 if (own_window.get(*state)) {
2227 /* if an ordinary window with decorations */
2228 if ((own_window_type.get(*state) == TYPE_NORMAL) &&
2229 !TEST_HINT(own_window_hints.get(*state), HINT_UNDECORATED)) {
2230 /* allow conky to hold input focus. */
2231 break;
2232 }
2233 /* forward the release to the desktop window */
2234 ev.xbutton.window = window.desktop;
2235 ev.xbutton.x = ev.xbutton.x_root;
2236 ev.xbutton.y = ev.xbutton.y_root;
2237 XSendEvent(display, ev.xbutton.window, False, ButtonReleaseMask,
2238 &ev);
2239 }
2240 break;
2241
2242 #endif
2243
2244 default:
2245 #ifdef BUILD_XDAMAGE
2246 if (ev.type == x11_stuff.event_base + XDamageNotify) {
2247 auto *dev = reinterpret_cast<XDamageNotifyEvent *>(&ev);
2248
2249 XFixesSetRegion(display, x11_stuff.part, &dev->area, 1);
2250 XFixesUnionRegion(display, x11_stuff.region2, x11_stuff.region2,
2251 x11_stuff.part);
2252 }
2253 #endif /* BUILD_XDAMAGE */
2254 break;
2255 }
2256 }
2257
2258 #ifdef BUILD_XDAMAGE
2259 if (x11_stuff.damage) {
2260 XDamageSubtract(display, x11_stuff.damage, x11_stuff.region2, None);
2261 XFixesSetRegion(display, x11_stuff.region2, nullptr, 0);
2262 }
2263 #endif /* BUILD_XDAMAGE */
2264
2265 /* XDBE doesn't seem to provide a way to clear the back buffer
2266 * without interfering with the front buffer, other than passing
2267 * XdbeBackground to XdbeSwapBuffers. That means that if we're
2268 * using XDBE, we need to redraw the text even if it wasn't part of
2269 * the exposed area. OTOH, if we're not going to call draw_stuff at
2270 * all, then no swap happens and we can safely do nothing. */
2271
2272 if (XEmptyRegion(x11_stuff.region) == 0) {
2273 #if defined(BUILD_XDBE)
2274 if (use_xdbe.get(*state)) {
2275 #else
2276 if (use_xpmdb.get(*state)) {
2277 #endif
2278 XRectangle r;
2279 int border_total = get_border_total();
2280
2281 r.x = text_start_x - border_total;
2282 r.y = text_start_y - border_total;
2283 r.width = text_width + 2 * border_total;
2284 r.height = text_height + 2 * border_total;
2285 XUnionRectWithRegion(&r, x11_stuff.region, x11_stuff.region);
2286 }
2287 XSetRegion(display, window.gc, x11_stuff.region);
2288 #ifdef BUILD_XFT
2289 if (use_xft.get(*state)) {
2290 XftDrawSetClip(window.xftdraw, x11_stuff.region);
2291 }
2292 #endif
2293 draw_stuff();
2294 XDestroyRegion(x11_stuff.region);
2295 x11_stuff.region = XCreateRegion();
2296 }
2297 } else {
2298 #endif /* BUILD_X11 */
2299 struct timespec req, rem;
2300 auto time_to_sleep = next_update_time - get_time();
2301 auto seconds = static_cast<time_t>(std::floor(time_to_sleep));
2302 auto nanos = (time_to_sleep - seconds) * 1000000000L;
2303 req.tv_sec = seconds;
2304 req.tv_nsec = nanos;
2305 nanosleep(&req, &rem);
2306 update_text();
2307 draw_stuff();
2308 #ifdef BUILD_NCURSES
2309 if (out_to_ncurses.get(*state)) {
2310 refresh();
2311 clear();
2312 }
2313 #endif
2314 #ifdef BUILD_X11
2315 }
2316 #endif /* BUILD_X11 */
2317
2318 #ifdef SIGNAL_BLOCKING
2319 /* unblock signals of interest and let handler fly */
2320 if (sigprocmask(SIG_SETMASK, &oldmask, nullptr) < 0) {
2321 CRIT_ERR(nullptr, NULL, "unable to sigprocmask()");
2322 }
2323 #endif
2324
2325 if (g_sighup_pending != 0) {
2326 g_sighup_pending = 0;
2327 NORM_ERR("received SIGUSR1. reloading the config file.");
2328
2329 reload_config();
2330 }
2331
2332 if (g_sigusr2_pending != 0) {
2333 g_sigusr2_pending = 0;
2334 // refresh view;
2335 NORM_ERR("received SIGUSR2. refreshing.");
2336 update_text();
2337 draw_stuff();
2338 #ifdef BUILD_NCURSES
2339 if (out_to_ncurses.get(*state)) {
2340 refresh();
2341 clear();
2342 }
2343 #endif
2344 }
2345
2346 if (g_sigterm_pending != 0) {
2347 g_sigterm_pending = 0;
2348 NORM_ERR("received SIGHUP, SIGINT, or SIGTERM to terminate. bye!");
2349 terminate = 1;
2350 #ifdef BUILD_X11
2351 if (out_to_x.get(*state)) {
2352 XDestroyRegion(x11_stuff.region);
2353 x11_stuff.region = nullptr;
2354 #ifdef BUILD_XDAMAGE
2355 if (x11_stuff.damage) {
2356 XDamageDestroy(display, x11_stuff.damage);
2357 XFixesDestroyRegion(display, x11_stuff.region2);
2358 XFixesDestroyRegion(display, x11_stuff.part);
2359 }
2360 #endif /* BUILD_XDAMAGE */
2361 }
2362 #endif /* BUILD_X11 */
2363 }
2364 #ifdef HAVE_SYS_INOTIFY_H
2365 if (!disable_auto_reload.get(*state) && inotify_fd != -1 &&
2366 inotify_config_wd == -1 && !current_config.empty()) {
2367 inotify_config_wd =
2368 inotify_add_watch(inotify_fd, current_config.c_str(), IN_MODIFY);
2369 }
2370 if (!disable_auto_reload.get(*state) && inotify_fd != -1 &&
2371 inotify_config_wd != -1 && !current_config.empty()) {
2372 int len = 0, idx = 0;
2373 fd_set descriptors;
2374 struct timeval time_to_wait;
2375
2376 FD_ZERO(&descriptors);
2377 FD_SET(inotify_fd, &descriptors);
2378
2379 time_to_wait.tv_sec = time_to_wait.tv_usec = 0;
2380
2381 select(inotify_fd + 1, &descriptors, nullptr, NULL, &time_to_wait);
2382 if (FD_ISSET(inotify_fd, &descriptors)) {
2383 /* process inotify events */
2384 len = read(inotify_fd, inotify_buff, INOTIFY_BUF_LEN - 1);
2385 inotify_buff[len] = 0;
2386 while (len > 0 && idx < len) {
2387 struct inotify_event *ev = (struct inotify_event *)&inotify_buff[idx];
2388 if (ev->wd == inotify_config_wd &&
2389 (ev->mask & IN_MODIFY || ev->mask & IN_IGNORED)) {
2390 /* current_config should be reloaded */
2391 NORM_ERR("'%s' modified, reloading...", current_config.c_str());
2392 reload_config();
2393 if (ev->mask & IN_IGNORED) {
2394 /* for some reason we get IN_IGNORED here
2395 * sometimes, so we need to re-add the watch */
2396 inotify_config_wd = inotify_add_watch(
2397 inotify_fd, current_config.c_str(), IN_MODIFY);
2398 }
2399 break;
2400 } else {
2401 llua_inotify_query(ev->wd, ev->mask);
2402 }
2403 idx += INOTIFY_EVENT_SIZE + ev->len;
2404 }
2405 }
2406 } else if (disable_auto_reload.get(*state) && inotify_fd != -1) {
2407 inotify_rm_watch(inotify_fd, inotify_config_wd);
2408 close(inotify_fd);
2409 inotify_fd = inotify_config_wd = -1;
2410 }
2411 #endif /* HAVE_SYS_INOTIFY_H */
2412
2413 llua_update_info(&info, active_update_interval());
2414 }
2415 clean_up(nullptr, nullptr);
2416
2417 #ifdef HAVE_SYS_INOTIFY_H
2418 if (inotify_fd != -1) {
2419 inotify_rm_watch(inotify_fd, inotify_config_wd);
2420 close(inotify_fd);
2421 inotify_fd = inotify_config_wd = -1;
2422 }
2423 #endif /* HAVE_SYS_INOTIFY_H */
2424 }
2425
2426 /* reload the config file */
2427 static void reload_config() {
2428 struct stat sb {};
2429 if ((stat(current_config.c_str(), &sb) != 0) ||
2430 (!S_ISREG(sb.st_mode) && !S_ISLNK(sb.st_mode))) {
2431 NORM_ERR(_("Config file '%s' is gone, continuing with config from "
2432 "memory.\nIf you recreate this file sent me a SIGUSR1 to tell "
2433 "me about it. ( kill -s USR1 %d )"),
2434 current_config.c_str(), getpid());
2435 return;
2436 }
2437 clean_up(nullptr, nullptr);
2438 state = std::make_unique<lua::state>();
2439 conky::export_symbols(*state);
2440 sleep(1); /* slight pause */
2441 initialisation(argc_copy, argv_copy);
2442 }
2443
2444 #ifdef BUILD_X11
2445 void clean_up_x11() {
2446 if (window_created == 1) {
2447 int border_total = get_border_total();
2448
2449 XClearArea(display, window.window, text_start_x - border_total,
2450 text_start_y - border_total, text_width + 2 * border_total,
2451 text_height + 2 * border_total, 0);
2452 }
2453 destroy_window();
2454 free_fonts(utf8_mode.get(*state));
2455 if (x11_stuff.region != nullptr) {
2456 XDestroyRegion(x11_stuff.region);
2457 x11_stuff.region = nullptr;
2458 }
2459 }
2460 #endif
2461
2462 void free_specials(special_t *¤t) {
2463 if (current != nullptr) {
2464 free_specials(current->next);
2465 if (current->type == GRAPH) { free(current->graph); }
2466 delete current;
2467 current = nullptr;
2468 }
2469
2470 clear_stored_graphs();
2471 }
2472
2473 void clean_up_without_threads(void *memtofree1, void *memtofree2) {
2474 free_and_zero(memtofree1);
2475 free_and_zero(memtofree2);
2476
2477 free_and_zero(info.cpu_usage);
2478 #ifdef BUILD_X11
2479 if (out_to_x.get(*state)) {
2480 clean_up_x11();
2481 } else {
2482 fonts.clear(); // in set_default_configurations a font is set but not
2483 }
2484 // loaded
2485 #endif /* BUILD_X11 */
2486
2487 if (info.first_process != nullptr) {
2488 free_all_processes();
2489 info.first_process = nullptr;
2490 }
2491
2492 free_text_objects(&global_root_object);
2493 delete_block_and_zero(tmpstring1);
2494 delete_block_and_zero(tmpstring2);
2495 delete_block_and_zero(text_buffer);
2496 free_and_zero(global_text);
2497
2498 #ifdef BUILD_PORT_MONITORS
2499 tcp_portmon_clear();
2500 #endif
2501 llua_shutdown_hook();
2502 #if defined BUILD_RSS
2503 xmlCleanupParser();
2504 #endif
2505
2506 free_specials(specials);
2507
2508 clear_net_stats();
2509 clear_fs_stats();
2510 clear_diskio_stats();
2511 free_and_zero(global_cpu);
2512
2513 conky::cleanup_config_settings(*state);
2514 state.reset();
2515 }
2516
2517 void clean_up(void *memtofree1, void *memtofree2) {
2518 /* free_update_callbacks(); XXX: some new equivalent of this? */
2519 clean_up_without_threads(memtofree1, memtofree2);
2520 }
2521
2522 static void set_default_configurations() {
2523 update_uname();
2524 info.memmax = 0;
2525 top_cpu = 0;
2526 top_mem = 0;
2527 top_time = 0;
2528 #ifdef BUILD_IOSTATS
2529 top_io = 0;
2530 #endif
2531 top_running = 0;
2532 #ifdef BUILD_XMMS2
2533 info.xmms2.artist = nullptr;
2534 info.xmms2.album = nullptr;
2535 info.xmms2.title = nullptr;
2536 info.xmms2.genre = nullptr;
2537 info.xmms2.comment = nullptr;
2538 info.xmms2.url = nullptr;
2539 info.xmms2.status = nullptr;
2540 info.xmms2.playlist = nullptr;
2541 #endif /* BUILD_XMMS2 */
2542 state->pushboolean(true);
2543 #ifdef BUILD_X11
2544 out_to_x.lua_set(*state);
2545 #else
2546 out_to_stdout.lua_set(*state);
2547 #endif
2548
2549 info.users.number = 1;
2550 }
2551
2552 #ifdef BUILD_X11
2553 static void X11_create_window() {
2554 if (out_to_x.get(*state)) {
2555 setup_fonts();
2556 load_fonts(utf8_mode.get(*state));
2557 #ifdef BUILD_XFT
2558 if (use_xft.get(*state)) {
2559 auto dpi = XGetDefault(display, "Xft", "dpi");
2560 if (dpi) { xft_dpi = atoi(dpi); }
2561 }
2562 #endif /* BUILD_XFT */
2563 update_text_area(); /* to position text/window on screen */
2564
2565 #ifdef OWN_WINDOW
2566 if (own_window.get(*state)) {
2567 if (fixed_pos == 0) {
2568 XMoveWindow(display, window.window, window.x, window.y);
2569 }
2570
2571 set_transparent_background(window.window);
2572 }
2573 #endif
2574
2575 create_gc();
2576
2577 draw_stuff();
2578
2579 x11_stuff.region = XCreateRegion();
2580 #ifdef BUILD_XDAMAGE
2581 if (XDamageQueryExtension(display, &x11_stuff.event_base,
2582 &x11_stuff.error_base) == 0) {
2583 NORM_ERR("Xdamage extension unavailable");
2584 x11_stuff.damage = 0;
2585 } else {
2586 x11_stuff.damage =
2587 XDamageCreate(display, window.window, XDamageReportNonEmpty);
2588 x11_stuff.region2 =
2589 XFixesCreateRegionFromWindow(display, window.window, 0);
2590 x11_stuff.part = XFixesCreateRegionFromWindow(display, window.window, 0);
2591 }
2592 #endif /* BUILD_XDAMAGE */
2593
2594 selected_font = 0;
2595 update_text_area(); /* to get initial size of the window */
2596 }
2597 /* setup lua window globals */
2598 llua_setup_window_table(text_start_x, text_start_y, text_width, text_height);
2599 }
2600 #endif /* BUILD_X11 */
2601
2602 void load_config_file() {
2603 DBGP(_("reading contents from config file '%s'"), current_config.c_str());
2604
2605 lua::state &l = *state;
2606 lua::stack_sentry s(l);
2607 l.checkstack(2);
2608
2609 try {
2610 #ifdef BUILD_BUILTIN_CONFIG
2611 if (current_config == builtin_config_magic) {
2612 l.loadstring(defconfig);
2613 } else {
2614 #endif
2615 l.loadfile(current_config.c_str());
2616 #ifdef BUILD_BUILTIN_CONFIG
2617 }
2618 #endif
2619 } catch (lua::syntax_error &e) {
2620 #define SYNTAX_ERR_READ_CONF "Syntax error (%s) while reading config file. "
2621 #ifdef BUILD_OLD_CONFIG
2622 NORM_ERR(_(SYNTAX_ERR_READ_CONF), e.what());
2623 NORM_ERR(_("Assuming it's in old syntax and attempting conversion."));
2624 // the strchr thingy skips the first line (#! /usr/bin/lua)
2625 l.loadstring(strchr(convertconf, '\n'));
2626 l.pushstring(current_config.c_str());
2627 l.call(1, 1);
2628 #else
2629 char *syntaxerr;
2630 if (asprintf(&syntaxerr, _(SYNTAX_ERR_READ_CONF), e.what())) {
2631 std::string syntaxerrobj(syntaxerr);
2632 free(syntaxerr);
2633 throw conky::error(syntaxerrobj);
2634 }
2635 #endif
2636 }
2637 l.call(0, 0);
2638
2639 l.getglobal("conky");
2640 l.getfield(-1, "text");
2641 l.replace(-2);
2642 if (l.type(-1) != lua::TSTRING) {
2643 throw conky::error(_("missing text block in configuration"));
2644 }
2645
2646 /* Remove \\-\n. */
2647 l.gsub(l.tocstring(-1), "\\\n", "");
2648 l.replace(-2);
2649 global_text = strdup(l.tocstring(-1));
2650 l.pop();
2651 }
2652
2653 inline void reset_optind() {
2654 #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || \
2655 defined(__OpenBSD__) || defined(__NetBSD__) || defined(__DragonFly__)
2656 optind = optreset = 1;
2657 #else
2658 optind = 0;
2659 #endif
2660 }
2661
2662 void set_current_config() {
2663 /* load current_config, CONFIG_FILE or SYSTEM_CONFIG_FILE */
2664 struct stat s {};
2665
2666 if (current_config.empty()) {
2667 /* Try to use personal config file first */
2668 std::string buf = to_real_path(XDG_CONFIG_FILE);
2669 if (stat(buf.c_str(), &s) == 0) { current_config = buf; }
2670 }
2671
2672 if (current_config.empty()) {
2673 /* Try to use personal config file first */
2674 std::string buf = to_real_path(CONFIG_FILE);
2675 if (stat(buf.c_str(), &s) == 0) { current_config = buf; }
2676 }
2677
2678 /* Try to use system config file if personal config does not exist */
2679 if (current_config.empty() && (stat(SYSTEM_CONFIG_FILE, &s) == 0)) {
2680 current_config = SYSTEM_CONFIG_FILE;
2681 }
2682
2683 /* No readable config found */
2684 if (current_config.empty()) {
2685 #define NOCFGFILEFOUND "no personal or system-wide config file found"
2686 #ifdef BUILD_BUILTIN_CONFIG
2687 current_config = builtin_config_magic;
2688 NORM_ERR(NOCFGFILEFOUND ", using builtin default");
2689 #else
2690 throw conky::error(NOCFGFILEFOUND);
2691 #endif
2692 }
2693
2694 // "-" stands for "read from stdin"
2695 if (current_config == "-") { current_config = "/dev/stdin"; }
2696 }
2697
2698 /* : means that character before that takes an argument */
2699 const char *getopt_string =
2700 "vVqdDSs:t:u:i:hc:p:"
2701 #ifdef BUILD_X11
2702 "x:y:w:a:X:m:f:"
2703 #ifdef OWN_WINDOW
2704 "o"
2705 #endif
2706 "b"
2707 #endif /* BUILD_X11 */
2708 #ifdef BUILD_BUILTIN_CONFIG
2709 "C"
2710 #endif
2711 ;
2712
2713 const struct option longopts[] = {
2714 {"help", 0, nullptr, 'h'}, {"version", 0, nullptr, 'V'},
2715 {"quiet", 0, nullptr, 'q'}, {"debug", 0, nullptr, 'D'},
2716 {"config", 1, nullptr, 'c'},
2717 #ifdef BUILD_BUILTIN_CONFIG
2718 {"print-config", 0, nullptr, 'C'},
2719 #endif
2720 {"daemonize", 0, nullptr, 'd'},
2721 #ifdef BUILD_X11
2722 {"alignment", 1, nullptr, 'a'}, {"display", 1, nullptr, 'X'},
2723 {"xinerama-head", 1, nullptr, 'm'}, {"font", 1, nullptr, 'f'},
2724 #ifdef OWN_WINDOW
2725 {"own-window", 0, nullptr, 'o'},
2726 #endif
2727 {"double-buffer", 0, nullptr, 'b'}, {"window-id", 1, nullptr, 'w'},
2728 #endif /* BUILD_X11 */
2729 {"text", 1, nullptr, 't'}, {"interval", 1, nullptr, 'u'},
2730 {"pause", 1, nullptr, 'p'}, {nullptr, 0, nullptr, 0}};
2731
2732 void setup_inotify() {
2733 #ifdef HAVE_SYS_INOTIFY_H
2734 // the file descriptor will be automatically closed on exit
2735 inotify_fd = inotify_init();
2736 if (inotify_fd != -1) {
2737 fcntl(inotify_fd, F_SETFL, fcntl(inotify_fd, F_GETFL) | O_NONBLOCK);
2738
2739 fcntl(inotify_fd, F_SETFD, fcntl(inotify_fd, F_GETFD) | FD_CLOEXEC);
2740 }
2741 #endif /* HAVE_SYS_INOTIFY_H */
2742 }
2743 void initialisation(int argc, char **argv) {
2744 struct sigaction act {
2745 }, oact{};
2746
2747 clear_net_stats();
2748 set_default_configurations();
2749
2750 set_current_config();
2751 load_config_file();
2752
2753 /* handle other command line arguments */
2754
2755 reset_optind();
2756
2757 #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
2758 if ((kd = kvm_open("/dev/null", "/dev/null", "/dev/null", O_RDONLY,
2759 "kvm_open")) == nullptr) {
2760 CRIT_ERR(nullptr, NULL, "cannot read kvm");
2761 }
2762 #endif
2763
2764 while (1) {
2765 int c = getopt_long(argc, argv, getopt_string, longopts, nullptr);
2766 int startup_pause;
2767 char *conv_end;
2768
2769 if (c == -1) { break; }
2770
2771 switch (c) {
2772 case 'd':
2773 state->pushboolean(true);
2774 fork_to_background.lua_set(*state);
2775 break;
2776 #ifdef BUILD_X11
2777 case 'f':
2778 state->pushstring(optarg);
2779 font.lua_set(*state);
2780 break;
2781 case 'a':
2782 state->pushstring(optarg);
2783 text_alignment.lua_set(*state);
2784 break;
2785 case 'm':
2786 state->pushinteger(strtol(optarg, &conv_end, 10));
2787 if (*conv_end != 0) {
2788 CRIT_ERR(nullptr, nullptr, "'%s' is a wrong xinerama-head index",
2789 optarg);
2790 }
2791 head_index.lua_set(*state);
2792 break;
2793 case 'X':
2794 state->pushstring(optarg);
2795 display_name.lua_set(*state);
2796 break;
2797
2798 #ifdef OWN_WINDOW
2799 case 'o':
2800 state->pushboolean(true);
2801 own_window.lua_set(*state);
2802 break;
2803 #endif
2804 #ifdef BUILD_XDBE
2805 case 'b':
2806 state->pushboolean(true);
2807 use_xdbe.lua_set(*state);
2808 break;
2809 #else
2810 case 'b':
2811 state->pushboolean(true);
2812 use_xpmdb.lua_set(*state);
2813 break;
2814 #endif
2815 #endif /* BUILD_X11 */
2816 case 't':
2817 free_and_zero(global_text);
2818 global_text = strndup(optarg, max_user_text.get(*state));
2819 convert_escapes(global_text);
2820 break;
2821
2822 case 'u':
2823 state->pushinteger(xft_dpi_scale(strtol(optarg, &conv_end, 10)));
2824 if (*conv_end != 0) {
2825 CRIT_ERR(nullptr, nullptr, "'%s' is a wrong update-interval", optarg);
2826 }
2827 update_interval.lua_set(*state);
2828 break;
2829
2830 case 'i':
2831 state->pushinteger(strtol(optarg, &conv_end, 10));
2832 if (*conv_end != 0) {
2833 CRIT_ERR(nullptr, nullptr, "'%s' is a wrong number of update-times",
2834 optarg);
2835 }
2836 total_run_times.lua_set(*state);
2837 break;
2838 #ifdef BUILD_X11
2839 case 'x':
2840 state->pushinteger(strtol(optarg, &conv_end, 10));
2841 if (*conv_end != 0) {
2842 CRIT_ERR(nullptr, nullptr, "'%s' is a wrong value for the X-position",
2843 optarg);
2844 }
2845 gap_x.lua_set(*state);
2846 break;
2847
2848 case 'y':
2849 state->pushinteger(strtol(optarg, &conv_end, 10));
2850 if (*conv_end != 0) {
2851 CRIT_ERR(nullptr, nullptr, "'%s' is a wrong value for the Y-position",
2852 optarg);
2853 }
2854 gap_y.lua_set(*state);
2855 break;
2856 #endif /* BUILD_X11 */
2857 case 'p':
2858 if (first_pass != 0) {
2859 startup_pause = atoi(optarg);
2860 sleep(startup_pause);
2861 }
2862 break;
2863
2864 case '?':
2865 throw unknown_arg_throw();
2866 }
2867 }
2868
2869 conky::set_config_settings(*state);
2870
2871 #ifdef BUILD_X11
2872 if (out_to_x.get(*state)) { current_text_color = default_color.get(*state); }
2873 #endif
2874
2875 /* generate text and get initial size */
2876 extract_variable_text(global_text);
2877 free_and_zero(global_text);
2878 /* fork */
2879 if (fork_to_background.get(*state) && (first_pass != 0)) {
2880 int pid = fork();
2881
2882 switch (pid) {
2883 case -1:
2884 NORM_ERR(PACKAGE_NAME ": couldn't fork() to background: %s",
2885 strerror(errno));
2886 break;
2887
2888 case 0:
2889 /* child process */
2890 usleep(25000);
2891 fprintf(stderr, "\n");
2892 fflush(stderr);
2893 break;
2894
2895 default:
2896 /* parent process */
2897 fprintf(stderr, PACKAGE_NAME ": forked to background, pid is %d\n",
2898 pid);
2899 fflush(stderr);
2900 throw fork_throw();
2901 }
2902 }
2903
2904 text_buffer = new char[max_user_text.get(*state)];
2905 memset(text_buffer, 0, max_user_text.get(*state));
2906 tmpstring1 = new char[text_buffer_size.get(*state)];
2907 memset(tmpstring1, 0, text_buffer_size.get(*state));
2908 tmpstring2 = new char[text_buffer_size.get(*state)];
2909 memset(tmpstring2, 0, text_buffer_size.get(*state));
2910
2911 #ifdef BUILD_X11
2912 X11_create_window();
2913 #endif /* BUILD_X11 */
2914 llua_setup_info(&info, active_update_interval());
2915
2916 /* Set signal handlers */
2917 act.sa_handler = signal_handler;
2918 sigemptyset(&act.sa_mask);
2919 act.sa_flags = 0;
2920 #ifdef SA_RESTART
2921 act.sa_flags |= SA_RESTART;
2922 #endif
2923
2924 if (sigaction(SIGINT, &act, &oact) < 0 ||
2925 sigaction(SIGALRM, &act, &oact) < 0 ||
2926 sigaction(SIGUSR1, &act, &oact) < 0 ||
2927 sigaction(SIGUSR2, &act, &oact) < 0 ||
2928 sigaction(SIGHUP, &act, &oact) < 0 ||
2929 sigaction(SIGTERM, &act, &oact) < 0) {
2930 NORM_ERR("error setting signal handler: %s", strerror(errno));
2931 }
2932
2933 llua_startup_hook();
2934 }
2935
2936 static void signal_handler(int sig) {
2937 /* signal handler is light as a feather, as it should be.
2938 * we will poll g_signal_pending with each loop of conky
2939 * and do any signal processing there, NOT here */
2940
2941 switch (sig) {
2942 case SIGHUP:
2943 case SIGINT:
2944 case SIGTERM:
2945 g_sigterm_pending = 1;
2946 break;
2947 case SIGUSR1:
2948 g_sighup_pending = 1;
2949 break;
2950 case SIGUSR2:
2951 g_sigusr2_pending = 1;
2952 default:
2953 /* Reaching here means someone set a signal
2954 * (SIGXXXX, signal_handler), but didn't write any code
2955 * to deal with it.
2956 * If you don't want to handle a signal, don't set a handler on
2957 * it in the first place.
2958 * We cannot print debug messages from a sighandler, so simply ignore.
2959 */
2960 break;
2961 }
2962 }
2963