/* torsmo, a system monitor
*
* This program is licensed under BSD license, read COPYING
*/
#include "torsmo.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <time.h>
#include <locale.h>
#include <signal.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <limits.h>
#if HAVE_DIRENT_H
#include <dirent.h>
#endif
#include <sys/time.h>
#include <X11/Xutil.h>
#define CONFIG_FILE "$HOME/.torsmorc"
#define MAIL_FILE "$MAIL"
/* alignments */
enum alignment {
TOP_LEFT = 1,
TOP_RIGHT,
BOTTOM_LEFT,
BOTTOM_RIGHT,
};
/* default config file */
static char *current_config;
/* set to 1 if you want all text to be in uppercase */
static unsigned int stuff_in_upper_case;
/* Position on the screen */
static int text_alignment;
static int gap_x, gap_y;
/* Font used */
static char *font_name;
/* Update interval */
static double update_interval;
/* fork? */
static int fork_to_background;
/* border */
static int draw_borders;
static int stippled_borders;
static int draw_shades, draw_outline;
static int border_margin, border_width;
static long default_fg_color, default_bg_color, default_out_color;
#ifdef OWN_WINDOW
/* create own window or draw stuff to root? */
static int own_window;
/* fixed size/pos is set if wm/user changes them */
static int fixed_size = 0, fixed_pos = 0;
#endif
static int minimum_width, minimum_height;
/* no buffers in used memory? */
int no_buffers;
/* pad percentages to decimals? */
static int pad_percents = 0;
/* Text that is shown */
static char original_text[] =
"$nodename - $sysname $kernel on $machine\n"
"$hr\n"
"${color grey}Uptime:$color $uptime\n"
"${color grey}Frequency (in MHz):$color $freq\n"
"${color grey}RAM Usage:$color $mem/$memmax - $memperc% ${membar 4}\n"
"${color grey}Swap Usage:$color $swap/$swapmax - $swapperc% ${swapbar 4}\n"
"${color grey}CPU Usage:$color $cpu% ${cpubar 4}\n"
"${color grey}Processes:$color $processes ${color grey}Running:$color $running_processes\n"
"$hr\n"
"${color grey}File systems:\n"
" / $color${fs_free /}/${fs_size /} ${fs_bar 6 /}\n"
"${color grey}Networking:\n"
" Up:$color ${upspeed eth0} k/s${color grey} - Down:$color ${downspeed eth0} k/s\n"
"${color grey}Temperatures:\n"
" CPU:$color ${i2c temp 1}°C${color grey} - MB:$color ${i2c temp 2}°C\n"
"$hr\n"
#ifdef SETI
"${color grey}SETI@Home Statistics:\n"
"${color grey}Seti Unit Number:$color $seti_credit\n"
"${color grey}Seti Progress:$color $seti_prog% $seti_progbar\n"
#endif
;
static char *text = original_text;
static int total_updates;
/* font stuff */
static XFontStruct *font;
#ifdef XFT
static XftFont *xftfont;
static int font_alpha = 65535;
#endif
static inline int calc_text_width(const char *s, unsigned int l) {
#ifdef XFT
if(use_xft) {
XGlyphInfo gi;
XftTextExtents8(display, xftfont, s, l, &gi);
return gi.xOff;
}
else
#endif
{
return XTextWidth(font, s, l);
}
}
#ifdef XFT
#define font_height() use_xft ? (xftfont->ascent + xftfont->descent) : \
(font->max_bounds.ascent + font->max_bounds.descent)
#define font_ascent() use_xft ? xftfont->ascent : font->max_bounds.ascent
#define font_descent() use_xft ? xftfont->descent : font->max_bounds.descent
#else
#define font_height() (font->max_bounds.ascent + font->max_bounds.descent)
#define font_ascent() font->max_bounds.ascent
#define font_descent() font->max_bounds.descent
#endif
/* formatted text to render on screen, generated in generate_text(),
* drawn in draw_stuff() */
#define TEXT_BUFFER_SIZE (1024*4)
static char text_buffer[TEXT_BUFFER_SIZE];
/* special stuff in text_buffer */
#define SPECIAL_CHAR '\x01'
enum {
HORIZONTAL_LINE,
STIPPLED_HR,
BAR,
FG,
BG,
OUTLINE,
};
static struct special_t {
int type;
short height;
short width;
long arg;
} specials[128];
static int special_count;
static int special_index; /* used when drawing */
static struct special_t *new_special(char *buf, int t) {
if (special_count >= 128)
CRIT_ERR("too much special things in text");
buf[0] = SPECIAL_CHAR;
buf[1] = '\0';
specials[special_count].type = t;
return &specials[special_count++];
}
static void new_bar(char *buf, int w, int h, int usage) {
struct special_t *s = new_special(buf, BAR);
s->arg = (usage > 255) ? 255 : ((usage < 0) ? 0 : usage);
s->width = w;
s->height = h;
}
static const char *scan_bar(const char *args, int *w, int *h) {
*w = 0; /* zero width means all space that is available */
*h = 4;
/* bar's argument is either height or height,width */
if (args) {
int n=0;
if (sscanf(args, "%d,%d %n", h, w, &n) <= 1)
sscanf(args, "%d %n", h, &n);
args += n;
}
return args;
}
static inline void new_hr(char *buf, int a) {
new_special(buf, HORIZONTAL_LINE)->height = a;
}
static inline void new_stippled_hr(char *buf, int a, int b) {
struct special_t *s = new_special(buf, STIPPLED_HR);
s->height = b;
s->arg = a;
}
static inline void new_fg(char *buf, long c) {
new_special(buf, FG)->arg = c;
}
static inline void new_bg(char *buf, long c) {
new_special(buf, BG)->arg = c;
}
static inline void new_outline(char *buf, long c) {
new_special(buf, OUTLINE)->arg = c;
}
/* quite boring functions */
static inline void for_each_line(char *b, void (*f)(char *)) {
char *ps, *pe;
for (ps=b, pe=b; *pe; pe++) {
if (*pe == '\n') {
*pe = '\0';
f(ps);
*pe = '\n';
ps = pe+1;
}
}
if (ps < pe) f(ps);
}
static void convert_escapes(char *buf) {
char *p = buf, *s = buf;
while (*s) {
if(*s == '\\') {
s++;
if(*s == 'n') *p++ = '\n';
else if(*s == '\\') *p++ = '\\';
s++;
}
else
*p++ = *s++;
}
*p = '\0';
}
/* converts from bytes to human readable format (k, M, G) */
static void human_readable(long long a, char *buf) {
if (a >= 1024*1024*1024)
snprintf(buf, 255, "%.2fG", (a/1024/1024)/1024.0);
else if (a >= 1024*1024) {
double m = (a/1024)/1024.0;
if(m >= 100.0)
snprintf(buf, 255, "%.0fM", m);
else
snprintf(buf, 255, "%.1fM", m);
}
else if (a >= 1024)
snprintf(buf, 255, "%Ldk", a/1024);
else
snprintf(buf, 255, "%Ld", a);
}
/* text handling */
enum text_object_type {
OBJ_acpiacadapter,
OBJ_adt746xcpu,
OBJ_adt746xfan,
OBJ_acpifan,
OBJ_acpitemp,
OBJ_battery,
OBJ_buffers,
OBJ_cached,
OBJ_color,
OBJ_cpu,
OBJ_cpubar,
OBJ_downspeed,
OBJ_downspeedf,
OBJ_exec,
OBJ_execi,
OBJ_freq,
OBJ_fs_bar,
OBJ_fs_bar_free,
OBJ_fs_free,
OBJ_fs_free_perc,
OBJ_fs_size,
OBJ_fs_used,
OBJ_fs_used_perc,
OBJ_hr,
OBJ_i2c,
OBJ_kernel,
OBJ_loadavg,
OBJ_machine,
OBJ_mails,
OBJ_mem,
OBJ_membar,
OBJ_memmax,
OBJ_memperc,
OBJ_mixer,
OBJ_mixerl,
OBJ_mixerr,
OBJ_mixerbar,
OBJ_mixerlbar,
OBJ_mixerrbar,
OBJ_new_mails,
OBJ_nodename,
#ifdef NVCTRL
OBJ_nvctrl,
#endif
OBJ_pre_exec,
OBJ_processes,
OBJ_running_processes,
OBJ_shadecolor,
OBJ_outlinecolor,
OBJ_stippled_hr,
OBJ_swap,
OBJ_swapbar,
OBJ_swapmax,
OBJ_swapperc,
OBJ_sysname,
OBJ_temp1, /* i2c is used instead in these */
OBJ_temp2,
OBJ_text,
OBJ_time,
OBJ_utime,
OBJ_totaldown,
OBJ_totalup,
OBJ_updates,
OBJ_upspeed,
OBJ_upspeedf,
OBJ_uptime,
OBJ_uptime_short,
#ifdef SETI
OBJ_seti_prog,
OBJ_seti_progbar,
OBJ_seti_credit
#endif
};
struct text_object {
int type;
union {
char *s; /* some string */
int i; /* some integer */
long l; /* some other integer */
struct net_stat *net;
struct fs_stat *fs;
unsigned char loadavg[3];
struct {
struct fs_stat *fs;
int w, h;
} fsbar; /* 3 */
struct {
int l;
int w, h;
} mixerbar; /* 3 */
struct {
int fd;
int arg;
} i2c; /* 2 */
struct {
double last_update;
float interval;
char *cmd;
char *buffer;
} execi; /* 5 */
struct {
int a, b;
} pair; /* 2 */
#ifdef NVCTRL
struct {
unsigned int arg;
} nvctrl; /* 1 */
#endif
} data;
};
static unsigned int text_object_count;
static struct text_object *text_objects;
/* new_text_object() allocates a new zeroed text_object */
static struct text_object *new_text_object() {
text_object_count++;
text_objects = (struct text_object *) realloc(text_objects,
sizeof(struct text_object) * text_object_count);
memset(&text_objects[text_object_count-1], 0, sizeof(struct text_object));
return &text_objects[text_object_count-1];
}
static void free_text_objects() {
unsigned int i;
for (i=0; i<text_object_count; i++) {
switch (text_objects[i].type) {
case OBJ_acpitemp:
close(text_objects[i].data.i);
break;
case OBJ_i2c:
close(text_objects[i].data.i2c.fd);
break;
case OBJ_time:
case OBJ_utime:
case OBJ_text:
case OBJ_exec:
case OBJ_pre_exec:
case OBJ_battery:
free(text_objects[i].data.s);
break;
case OBJ_execi:
free(text_objects[i].data.execi.cmd);
free(text_objects[i].data.execi.buffer);
break;
}
}
free(text_objects);
text_objects = NULL;
text_object_count = 0;
}
void scan_mixer_bar(const char *arg, int *a, int *w, int *h) {
char buf1[64];
int n;
if(arg && sscanf(arg, "%63s %n", buf1, &n) >= 1) {
*a = mixer_init(buf1);
(void) scan_bar(arg + n, w, h);
}
else {
*a = mixer_init(0);
(void) scan_bar(arg, w, h);
}
}
/* construct_text_object() creates a new text_object */
static void construct_text_object(const char *s, const char *arg) {
struct text_object *obj = new_text_object();
#define OBJ(a, n) if (strcmp(s, #a) == 0) { obj->type = OBJ_##a; need_mask |= (1 << n); {
#define END ; } } else
if (s[0] == '#') {
obj->type = OBJ_color;
obj->data.l = get_x11_color(s);
}
else
OBJ(acpitemp, 0)
obj->data.i = open_acpi_temperature(arg);
END
OBJ(acpiacadapter, 0)
END
OBJ(freq, 0)
END
OBJ(acpifan, 0)
END
OBJ(battery, 0)
char bat[64];
if (arg)
sscanf(arg, "%63s", bat);
else
strcpy(bat, "BAT0");
obj->data.s = strdup(bat);
END
OBJ(buffers, INFO_BUFFERS)
END
OBJ(cached, INFO_BUFFERS)
END
OBJ(cpu, INFO_CPU)
END
OBJ(cpubar, INFO_CPU)
(void) scan_bar(arg, &obj->data.pair.a, &obj->data.pair.b);
END
OBJ(color, 0)
obj->data.l = arg ? get_x11_color(arg) : default_fg_color;
END
OBJ(downspeed, INFO_NET)
obj->data.net = get_net_stat(arg);
END
OBJ(downspeedf, INFO_NET)
obj->data.net = get_net_stat(arg);
END
#ifdef HAVE_POPEN
OBJ(exec, 0)
obj->data.s = strdup(arg ? arg : "");
END
OBJ(execi, 0)
unsigned int n;
if (!arg || sscanf(arg, "%f %n", &obj->data.execi.interval, &n) <= 0) {
char buf[256];
ERR("${execi <interval> command}");
obj->type = OBJ_text;
snprintf(buf, 256, "${%s}", s);
obj->data.s = strdup(buf);
}
else {
obj->data.execi.cmd = strdup(arg + n);
obj->data.execi.buffer = (char *) calloc(1, TEXT_BUFFER_SIZE);
}
END
OBJ(pre_exec, 0)
obj->type = OBJ_text;
if (arg) {
FILE *fp = popen(arg, "r");
unsigned int n;
char buf[2048];
n = fread(buf, 1, 2048, fp);
buf[n] = '\0';
if(n && buf[n-1] == '\n') buf[n-1] = '\0';
(void) pclose(fp);
obj->data.s = strdup(buf);
}
else
obj->data.s = strdup("");
END
#endif
OBJ(fs_bar, INFO_FS)
obj->data.fsbar.h = 4;
arg = scan_bar(arg, &obj->data.fsbar.w, &obj->data.fsbar.h);
if (arg) {
while (isspace(*arg)) arg++;
if(*arg == '\0') arg = "/";
}
else
arg = "/";
obj->data.fsbar.fs = prepare_fs_stat(arg);
END
OBJ(fs_bar_free, INFO_FS)
obj->data.fsbar.h = 4;
if (arg) {
unsigned int n;
if (sscanf(arg, "%d %n", &obj->data.fsbar.h, &n) >= 1)
arg += n;
}
else
arg = "/";
obj->data.fsbar.fs = prepare_fs_stat(arg);
END
OBJ(fs_free, INFO_FS)
if (!arg) arg = "/";
obj->data.fs = prepare_fs_stat(arg);
END
OBJ(fs_used_perc, INFO_FS)
if (!arg) arg = "/";
obj->data.fs = prepare_fs_stat(arg);
END
OBJ(fs_free_perc, INFO_FS)
if (!arg) arg = "/";
obj->data.fs = prepare_fs_stat(arg);
END
OBJ(fs_size, INFO_FS)
if (!arg) arg = "/";
obj->data.fs = prepare_fs_stat(arg);
END
OBJ(fs_used, INFO_FS)
if (!arg) arg = "/";
obj->data.fs = prepare_fs_stat(arg);
END
OBJ(hr, 0)
obj->data.i = arg ? atoi(arg) : 1;
END
OBJ(i2c, INFO_I2C)
char buf1[64], buf2[64];
int n;
if(!arg) {
ERR("i2c needs arguments");
obj->type = OBJ_text;
obj->data.s = strdup("${i2c}");
return;
}
if(sscanf(arg, "%63s %63s %d", buf1, buf2, &n) != 3) {
/* if scanf couldn't read three values, read type and num and use
* default device */
sscanf(arg, "%63s %d", buf2, &n);
obj->data.i2c.fd = open_i2c_sensor(0, buf2, n, &obj->data.i2c.arg);
}
else {
obj->data.i2c.fd = open_i2c_sensor(buf1, buf2, n, &obj->data.i2c.arg);
}
END
OBJ(loadavg, INFO_LOADAVG)
int a = 1, b = 2, c = 3, r = 3;
if (arg) {
r = sscanf(arg, "%d %d %d", &a, &b, &c);
if (r >= 3 && (c < 1 || c > 3))
r--;
if (r >= 2 && (b < 1 || b > 3))
r--, b = c;
if (r >= 1 && (a < 1 || a > 3))
r--, a = b, b = c;
}
obj->data.loadavg[0] = (r >= 1) ? (unsigned char) a : 0;
obj->data.loadavg[1] = (r >= 2) ? (unsigned char) b : 0;
obj->data.loadavg[2] = (r >= 3) ? (unsigned char) c : 0;
END
OBJ(kernel, 0)
END
OBJ(machine, 0)
END
OBJ(mails, INFO_MAIL)
END
OBJ(mem, INFO_MEM)
END
OBJ(memmax, INFO_MEM)
END
OBJ(memperc, INFO_MEM)
END
OBJ(membar, INFO_MEM)
(void) scan_bar(arg, &obj->data.pair.a, &obj->data.pair.b);
END
OBJ(mixer, INFO_MIXER)
obj->data.l = mixer_init(arg);
END
OBJ(mixerl, INFO_MIXER)
obj->data.l = mixer_init(arg);
END
OBJ(mixerr, INFO_MIXER)
obj->data.l = mixer_init(arg);
END
OBJ(mixerbar, INFO_MIXER)
scan_mixer_bar(arg, &obj->data.mixerbar.l, &obj->data.mixerbar.w,
&obj->data.mixerbar.h);
END
OBJ(mixerlbar, INFO_MIXER)
scan_mixer_bar(arg, &obj->data.mixerbar.l, &obj->data.mixerbar.w,
&obj->data.mixerbar.h);
END
OBJ(mixerrbar, INFO_MIXER)
scan_mixer_bar(arg, &obj->data.mixerbar.l, &obj->data.mixerbar.w,
&obj->data.mixerbar.h);
END
OBJ(new_mails, INFO_MAIL)
END
OBJ(nodename, 0)
END
#ifdef NVCTRL
OBJ(nvctrl, 0)
obj->data.nvctrl.arg = init_nvctrl(arg);
END
#endif
OBJ(processes, INFO_PROCS)
END
OBJ(running_processes, INFO_RUN_PROCS)
END
OBJ(shadecolor, 0)
obj->data.l = arg ? get_x11_color(arg) : default_bg_color;
END
OBJ(outlinecolor, 0)
obj->data.l = arg ? get_x11_color(arg) : default_out_color;
END
OBJ(stippled_hr, 0)
int a = stippled_borders, b = 1;
if(arg) {
if(sscanf(arg, "%d %d", &a, &b) != 2)
sscanf(arg, "%d", &b);
}
if (a <= 0) a = 1;
obj->data.pair.a = a;
obj->data.pair.b = b;
END
OBJ(swap, INFO_MEM)
END
OBJ(swapmax, INFO_MEM)
END
OBJ(swapperc, INFO_MEM)
END
OBJ(swapbar, INFO_MEM)
(void) scan_bar(arg, &obj->data.pair.a, &obj->data.pair.b);
END
OBJ(sysname, 0)
END
OBJ(temp1, INFO_I2C)
obj->type = OBJ_i2c;
obj->data.i2c.fd = open_i2c_sensor(0, "temp", 1, &obj->data.i2c.arg);
END
OBJ(temp2, INFO_I2C)
obj->type = OBJ_i2c;
obj->data.i2c.fd = open_i2c_sensor(0, "temp", 2, &obj->data.i2c.arg);
END
OBJ(time, 0)
obj->data.s = strdup(arg ? arg : "%F %T");
END
OBJ(utime, 0)
obj->data.s = strdup(arg ? arg : "%F %T");
END
OBJ(totaldown, INFO_NET)
obj->data.net = get_net_stat(arg);
END
OBJ(totalup, INFO_NET)
obj->data.net = get_net_stat(arg);
END
OBJ(updates, 0)
END
OBJ(upspeed, INFO_NET)
obj->data.net = get_net_stat(arg);
END
OBJ(upspeedf, INFO_NET)
obj->data.net = get_net_stat(arg);
END
OBJ(uptime_short, INFO_UPTIME)
END
OBJ(uptime, INFO_UPTIME)
END
#ifdef SETI
OBJ(seti_prog, INFO_SETI)
END
OBJ(seti_progbar, INFO_SETI)
(void) scan_bar(arg, &obj->data.pair.a, &obj->data.pair.b);
END
OBJ(seti_credit, INFO_SETI)
END
#endif
{
char buf[256];
ERR("unknown variable %s", s);
obj->type = OBJ_text;
snprintf(buf, 256, "${%s}", s);
obj->data.s = strdup(buf);
}
#undef OBJ
}
/* append_text() appends text to last text_object if it's text, if it isn't
* it creates a new text_object */
static void append_text(const char *s) {
struct text_object *obj;
if (s == NULL || *s == '\0')
return;
obj = text_object_count ? &text_objects[text_object_count-1] : 0;
/* create a new text object? */
if (!obj || obj->type != OBJ_text) {
obj = new_text_object();
obj->type = OBJ_text;
obj->data.s = strdup(s);
}
else {
/* append */
obj->data.s = (char *) realloc(obj->data.s,
strlen(obj->data.s) + strlen(s) + 1);
strcat(obj->data.s, s);
}
}
static void extract_variable_text(const char *p) {
const char *s = p;
free_text_objects();
while (*p) {
if (*p == '$') {
* (char *) p = '\0';
append_text(s);
* (char *) p = '$';
p++;
s = p;
if (*p != '$') {
char buf[256];
const char *var;
unsigned int len;
/* variable is either $foo or ${foo} */
if (*p == '{') {
p++;
s = p;
while (*p && *p != '}') p++;
}
else {
s = p;
if (*p == '#') p++;
while (*p && (isalnum((int) *p) || *p=='_')) p++;
}
/* copy variable to buffer */
len = (p - s > 255) ? 255 : (p - s);
strncpy(buf, s, len);
buf[len] = '\0';
if (*p == '}') p++;
s = p;
var = getenv(buf);
/* if variable wasn't found from environment, use some special */
if (!var) {
char *p;
char *arg = 0;
/* split arg */
if (strchr(buf, ' ')) {
arg = strchr(buf, ' ');
*arg = '\0';
arg++;
while (isspace((int) *arg)) arg++;
if (!*arg) arg = 0;
}
/* lowercase variable name */
p = buf;
while (*p) {
*p = tolower(*p);
p++;
}
construct_text_object(buf, arg);
}
continue;
}
else
append_text("$");
}
p++;
}
append_text(s);
}
double current_update_time, last_update_time;
static void generate_text() {
unsigned int i, n;
struct information *cur = &info;
char *p;
special_count = 0;
/* update info */
current_update_time = get_time();
update_stuff();
/* generate text */
n = TEXT_BUFFER_SIZE - 2;
p = text_buffer;
for (i=0; i<text_object_count; i++) {
struct text_object *obj = &text_objects[i];
#define OBJ(a) break; case OBJ_##a:
switch (obj->type) {
default: {
ERR("not implemented obj type %d", obj->type);
}
OBJ(acpitemp) {
/* does anyone have decimals in acpi temperature? */
snprintf(p, n, "%0.1f", (float)get_acpi_temperature(obj->data.i));
}
OBJ(freq) {
snprintf(p, n, "%s", get_freq());
}
OBJ(adt746xcpu) {
snprintf(p, n, "%s", get_adt746x_cpu());
}
OBJ(adt746xfan) {
snprintf(p, n, "%s", get_adt746x_fan());
}
OBJ(acpifan) {
snprintf(p, n, "%s", get_acpi_fan());
}
OBJ(acpiacadapter) {
snprintf(p, n, "%s", get_acpi_ac_adapter());
}
OBJ(battery) {
get_battery_stuff(p, n, obj->data.s);
}
OBJ(buffers) {
human_readable(cur->buffers*1024, p);
}
OBJ(cached) {
human_readable(cur->cached*1024, p);
}
OBJ(cpu) {
snprintf(p, n, "%*d", pad_percents, (int) (cur->cpu_usage*100.0));
}
OBJ(cpubar) {
new_bar(p, obj->data.pair.a, obj->data.pair.b, (int) (cur->cpu_usage*255.0));
}
OBJ(color) {
new_fg(p, obj->data.l);
}
OBJ(downspeed) {
snprintf(p, n, "%d", (int) (obj->data.net->recv_speed/1024));
}
OBJ(downspeedf) {
snprintf(p, n, "%.1f", obj->data.net->recv_speed/1024.0);
}
#ifdef HAVE_POPEN
OBJ(exec) {
char *p2 = p;
FILE *fp = popen(obj->data.s, "r");
int n2 = fread(p, 1, n, fp);
(void) pclose(fp);
p[n2] = '\0';
if(n2 && p[n2-1] == '\n') p[n2-1] = '\0';
while (*p2) {
if (*p2 == '\001')
*p2 = ' ';
p2++;
}
}
OBJ(execi) {
if (current_update_time - obj->data.execi.last_update <
obj->data.execi.interval) {
snprintf(p, n, "%s", obj->data.execi.buffer);
}
else {
char *p2 = obj->data.execi.buffer;
FILE *fp = popen(obj->data.execi.cmd, "r");
int n2 = fread(p2, 1, TEXT_BUFFER_SIZE, fp);
(void) pclose(fp);
p2[n2] = '\0';
if(n2 && p2[n2-1] == '\n') p2[n2-1] = '\0';
while (*p2) {
if (*p2 == '\001')
*p2 = ' ';
p2++;
}
snprintf(p, n, "%s", obj->data.execi.buffer);
obj->data.execi.last_update = current_update_time;
}
}
#endif
OBJ(fs_bar) {
if (obj->data.fs != NULL) {
if (obj->data.fs->size == 0)
new_bar(p, obj->data.fsbar.w, obj->data.fsbar.h, 255);
else
new_bar(p, obj->data.fsbar.w, obj->data.fsbar.h,
(int) (255 - obj->data.fsbar.fs->avail*255/obj->data.fs->size));
}
}
OBJ(fs_free) {
if (obj->data.fs != NULL)
human_readable(obj->data.fs->avail, p);
}
OBJ(fs_free_perc) {
if (obj->data.fs != NULL) {
if (obj->data.fs->size)
snprintf(p, n, "%*d", pad_percents,
(int) ((obj->data.fs->avail*100) / obj->data.fs->size));
else
snprintf(p, n, "0");
}
}
OBJ(fs_size) {
if (obj->data.fs != NULL)
human_readable(obj->data.fs->size, p);
}
OBJ(fs_used) {
if (obj->data.fs != NULL)
human_readable(obj->data.fs->size - obj->data.fs->avail, p);
}
OBJ(fs_bar_free) {
if (obj->data.fs != NULL) {
if (obj->data.fs->size == 0)
new_bar(p, obj->data.fsbar.w, obj->data.fsbar.h, 255);
else
new_bar(p, obj->data.fsbar.w, obj->data.fsbar.h,
(int) (obj->data.fsbar.fs->avail*255/obj->data.fs->size));
}
}
OBJ(fs_used_perc) {
if (obj->data.fs != NULL) {
if (obj->data.fs->size)
snprintf(p, n, "%d",
100 - ((int) ((obj->data.fs->avail*100) / obj->data.fs->size)));
else
snprintf(p, n, "0");
}
}
OBJ(loadavg) {
float *v = info.loadavg;
if (obj->data.loadavg[2])
snprintf(p, n, "%.2f %.2f %.2f", v[obj->data.loadavg[0] - 1],
v[obj->data.loadavg[1] - 1], v[obj->data.loadavg[2] - 1]);
else if (obj->data.loadavg[1])
snprintf(p, n, "%.2f %.2f", v[obj->data.loadavg[0] - 1],
v[obj->data.loadavg[1] - 1]);
else if (obj->data.loadavg[0])
snprintf(p, n, "%.2f", v[obj->data.loadavg[0] - 1]);
}
OBJ(hr) {
new_hr(p, obj->data.i);
}
OBJ(i2c) {
double r;
r = get_i2c_info(obj->data.i2c.fd, obj->data.i2c.arg);
if (r >= 100.0 || r == 0)
snprintf(p, n, "%d", (int) r);
else
snprintf(p, n, "%.1f", r);
}
OBJ(kernel) {
snprintf(p, n, "%s", cur->uname_s.release);
}
OBJ(machine) {
snprintf(p, n, "%s", cur->uname_s.machine);
}
/* memory stuff */
OBJ(mem) {
human_readable(cur->mem*1024, p);
}
OBJ(memmax) {
human_readable(cur->memmax*1024, p);
}
OBJ(memperc) {
if (cur->memmax)
snprintf(p, n, "%*d", pad_percents, (cur->mem*100) / (cur->memmax));
}
OBJ(membar) {
new_bar(p, obj->data.pair.a, obj->data.pair.b,
cur->memmax ? (cur->mem*255) / (cur->memmax) : 0);
}
/* mixer stuff */
OBJ(mixer) {
snprintf(p, n, "%d", mixer_get_avg(obj->data.l));
}
OBJ(mixerl) {
snprintf(p, n, "%d", mixer_get_left(obj->data.l));
}
OBJ(mixerr) {
snprintf(p, n, "%d", mixer_get_right(obj->data.l));
}
OBJ(mixerbar) {
new_bar(p, obj->data.mixerbar.w, obj->data.mixerbar.h,
mixer_get_avg(obj->data.mixerbar.l)*255/100);
}
OBJ(mixerlbar) {
new_bar(p, obj->data.mixerbar.w, obj->data.mixerbar.h,
mixer_get_left(obj->data.mixerbar.l)*255/100);
}
OBJ(mixerrbar) {
new_bar(p, obj->data.mixerbar.w, obj->data.mixerbar.h,
mixer_get_right(obj->data.mixerbar.l)*255/100);
}
/* mail stuff */
OBJ(mails) {
snprintf(p, n, "%d", cur->mail_count);
}
OBJ(new_mails) {
snprintf(p, n, "%d", cur->new_mail_count);
}
OBJ(nodename) {
snprintf(p, n, "%s", cur->uname_s.nodename);
}
#ifdef NVCTRL
OBJ(nvctrl) {
snprintf(p, n, "%d", get_nvctrl_info(obj->data.nvctrl.arg));
}
#endif
OBJ(outlinecolor) {
new_outline(p, obj->data.l);
}
OBJ(processes) {
snprintf(p, n, "%d", cur->procs);
}
OBJ(running_processes) {
snprintf(p, n, "%d", cur->run_procs);
}
OBJ(text) {
snprintf(p, n, "%s", obj->data.s);
}
OBJ(shadecolor) {
new_bg(p, obj->data.l);
}
OBJ(stippled_hr) {
new_stippled_hr(p, obj->data.pair.a, obj->data.pair.b);
}
OBJ(swap) {
human_readable(cur->swap*1024, p);
}
OBJ(swapmax) {
human_readable(cur->swapmax*1024, p);
}
OBJ(swapperc) {
snprintf(p, 255, "%*u", pad_percents,
cur->swapmax ? (cur->swap*100) / cur->swapmax : 0);
}
OBJ(swapbar) {
new_bar(p, obj->data.pair.a, obj->data.pair.b,
cur->swapmax ? (cur->swap*255) / (cur->swapmax) : 0);
}
OBJ(sysname) {
snprintf(p, n, "%s", cur->uname_s.sysname);
}
OBJ(time) {
time_t t = time(NULL);
struct tm *tm = localtime(&t);
setlocale(LC_TIME, "");
strftime(p, n, obj->data.s, tm);
}
OBJ(utime) {
time_t t = time(NULL);
struct tm *tm = gmtime(&t);
strftime(p, n, obj->data.s, tm);
}
OBJ(totaldown) {
human_readable(obj->data.net->recv, p);
}
OBJ(totalup) {
human_readable(obj->data.net->trans, p);
}
OBJ(updates) {
snprintf(p, n, "%d", total_updates);
}
OBJ(upspeed) {
snprintf(p, n, "%d", (int) (obj->data.net->trans_speed/1024));
}
OBJ(upspeedf) {
snprintf(p, n, "%.1f", obj->data.net->trans_speed/1024.0);
}
OBJ(uptime_short) {
format_seconds_short(p, n, (int) cur->uptime);
}
OBJ(uptime) {
format_seconds(p, n, (int) cur->uptime);
}
#ifdef SETI
OBJ(seti_prog) {
snprintf(p, n, "%.2f", cur->seti_prog * 100.0f);
}
OBJ(seti_progbar) {
new_bar(p, obj->data.pair.a, obj->data.pair.b, (int)(cur->seti_prog * 255.0f));
}
OBJ(seti_credit) {
snprintf(p, n, "%.0f", cur->seti_credit);
}
#endif
break;
}
{
unsigned int a = strlen(p);
p += a;
n -= a;
}
}
if (stuff_in_upper_case) {
char *p;
p = text_buffer;
while (*p) {
*p = toupper(*p);
p++;
}
}
last_update_time = current_update_time;
total_updates++;
}
/*
* text size
*/
static int text_start_x, text_start_y; /* text start position in window */
static int text_width, text_height;
static inline int get_string_width(const char *s) {
return *s ? calc_text_width(s, strlen(s)) : 0;
}
static void text_size_updater(char *s) {
int w = 0;
char *p;
/* get string widths and skip specials */
p = s;
while (*p) {
if (*p == SPECIAL_CHAR) {
*p = '\0';
w += get_string_width(s);
*p = SPECIAL_CHAR;
if(specials[special_index].type == BAR) {
w += specials[special_index].width;
}
special_index++;
s = p+1;
}
p++;
}
w += get_string_width(s);
if (w > text_width) text_width = w;
text_height += font_height();
}
static void update_text_area() {
int x, y;
/* update text size if it isn't fixed */
#ifdef OWN_WINDOW
if (!fixed_size)
#endif
{
text_width = minimum_width;
text_height = 0;
special_index = 0;
for_each_line(text_buffer, text_size_updater);
text_width += 1;
if (text_height < minimum_height)
text_height = minimum_height;
}
/* get text position on workarea */
switch (text_alignment) {
case TOP_LEFT:
x = gap_x;
y = gap_y;
break;
case TOP_RIGHT:
x = workarea[2] - text_width - gap_x;
y = gap_y;
break;
default:
case BOTTOM_LEFT:
x = gap_x;
y = workarea[3] - text_height - gap_y;
break;
case BOTTOM_RIGHT:
x = workarea[2] - text_width - gap_x;
y = workarea[3] - text_height - gap_y;
break;
}
#ifdef OWN_WINDOW
if (own_window) {
x += workarea[0];
y += workarea[1];
text_start_x = border_margin + 1;
text_start_y = border_margin + 1;
window.x = x - border_margin - 1;
window.y = y - border_margin - 1;
}
else
#endif
{
/* If window size doesn't match to workarea's size, then window
* probably includes panels (gnome).
* Blah, doesn't work on KDE. */
if (workarea[2] != window.width || workarea[3] != window.height) {
y += workarea[1];
x += workarea[0];
}
text_start_x = x;
text_start_y = y;
}
}
/*
* drawing stuff
*/
static int cur_x, cur_y; /* current x and y for drawing */
static int draw_mode; /* FG, BG or OUTLINE */
static long current_color;
static inline void set_foreground_color(long c) {
current_color = c;
XSetForeground(display, window.gc, c);
}
static void draw_string(const char *s) {
if (s[0] == '\0') return;
#ifdef XFT
if(use_xft) {
XColor c;
XftColor c2;
c.pixel = current_color;
XQueryColor(display, DefaultColormap(display, screen), &c);
c2.pixel = c.pixel;
c2.color.red = c.red;
c2.color.green = c.green;
c2.color.blue = c.blue;
c2.color.alpha = font_alpha;
XftDrawString8(window.xftdraw, &c2, xftfont,
cur_x, cur_y, (XftChar8 *) s, strlen(s));
}
else
#endif
{
XDrawString(display, window.drawable, window.gc,
cur_x, cur_y, s, strlen(s));
}
cur_x += get_string_width(s);
}
static void draw_line(char *s) {
char *p;
cur_x = text_start_x;
cur_y += font_ascent();
/* find specials and draw stuff */
p = s;
while (*p) {
if (*p == SPECIAL_CHAR) {
int w = 0;
/* draw string before special */
*p = '\0';
draw_string(s);
*p = SPECIAL_CHAR;
s = p+1;
/* draw special */
switch (specials[special_index].type) {
case HORIZONTAL_LINE:
{
int h = specials[special_index].height;
int mid = font_ascent() / 2;
w = text_start_x + text_width - cur_x;
XSetLineAttributes(display, window.gc, h,
LineSolid, CapButt, JoinMiter);
XDrawLine(display, window.drawable, window.gc,
cur_x, cur_y-mid, cur_x+w, cur_y-mid);
}
break;
case STIPPLED_HR:
{
int h = specials[special_index].height;
int s = specials[special_index].arg;
int mid = font_ascent() / 2;
char ss[2] = { s, s };
w = text_start_x + text_width - cur_x - 1;
XSetLineAttributes(display, window.gc, h, LineOnOffDash, CapButt,
JoinMiter);
XSetDashes(display, window.gc, 0, ss, 2);
XDrawLine(display, window.drawable, window.gc,
cur_x, cur_y-mid, cur_x+w, cur_y-mid);
}
break;
case BAR:
{
int h = specials[special_index].height;
int bar_usage = specials[special_index].arg;
int by = cur_y - (font_ascent() + h)/2 + 1;
w = specials[special_index].width;
if(w == 0)
w = text_start_x + text_width - cur_x - 1;
if(w < 0) w = 0;
XSetLineAttributes(display, window.gc, 1, LineSolid, CapButt, JoinMiter);
XDrawRectangle(display, window.drawable, window.gc,
cur_x, by, w, h);
XFillRectangle(display, window.drawable, window.gc,
cur_x, by, w * bar_usage / 255, h);
}
break;
case FG:
if (draw_mode == FG)
set_foreground_color(specials[special_index].arg);
break;
case BG:
if (draw_mode == BG)
set_foreground_color(specials[special_index].arg);
break;
case OUTLINE:
if (draw_mode == OUTLINE)
set_foreground_color(specials[special_index].arg);
break;
}
cur_x += w;
special_index++;
}
p++;
}
draw_string(s);
cur_y += font_descent();
}
static void draw_text() {
cur_y = text_start_y;
/* draw borders */
if (draw_borders && border_width > 0) {
unsigned int b = (border_width+1)/2;
if(stippled_borders) {
char ss[2] = { stippled_borders, stippled_borders };
XSetLineAttributes(display, window.gc, border_width,
LineOnOffDash, CapButt, JoinMiter);
XSetDashes(display, window.gc, 0, ss, 2);
}
else {
XSetLineAttributes(display, window.gc, border_width,
LineSolid, CapButt, JoinMiter);
}
XDrawRectangle(display, window.drawable, window.gc,
text_start_x - border_margin + b,
text_start_y - border_margin + b,
text_width + border_margin*2 - 1 - b*2,
text_height + border_margin*2 - 1 - b*2);
}
/* draw text */
special_index = 0;
for_each_line(text_buffer, draw_line);
}
static void draw_stuff() {
if (draw_shades && !draw_outline) {
text_start_x++;
text_start_y++;
set_foreground_color(default_bg_color);
draw_mode = BG;
draw_text();
text_start_x--;
text_start_y--;
}
if (draw_outline) {
int i, j;
for (i=-1;i<2;i++)
for (j=-1;j<2;j++) {
if (i==0 && j==0)
continue;
text_start_x+=i;
text_start_y+=j;
set_foreground_color(default_out_color);
draw_mode = OUTLINE;
draw_text();
text_start_x-=i;
text_start_y-=j;
}
}
set_foreground_color(default_fg_color);
draw_mode = FG;
draw_text();
#ifdef XDBE
if (use_xdbe) {
XdbeSwapInfo swap;
swap.swap_window = window.window;
swap.swap_action = XdbeBackground;
XdbeSwapBuffers(display, &swap, 1);
}
#endif
}
static void clear_text(int exposures) {
#ifdef XDBE
if (use_xdbe) return; /* The swap action is XdbeBackground, which clears */
#endif
/* there is some extra space for borders and outlines */
XClearArea(display, window.drawable,
text_start_x-border_margin-1, text_start_y-border_margin-1,
text_width+border_margin*2+2, text_height+border_margin*2+2,
exposures ? True : 0);
}
static int need_to_update;
/* update_text() generates new text and clears old text area */
static void update_text() {
generate_text();
clear_text(1);
need_to_update = 1;
}
static void main_loop() {
Region region = XCreateRegion();
while (1) {
XFlush(display);
/* wait for X event or timeout */
if (!XPending(display)) {
fd_set fdsr;
struct timeval tv;
int s;
double t = update_interval - (get_time() - last_update_time);
if (t < 0) t = 0;
tv.tv_sec = (long) t;
tv.tv_usec = (long) (t * 1000000) % 1000000;
FD_ZERO(&fdsr);
FD_SET(ConnectionNumber(display), &fdsr);
s = select(ConnectionNumber(display) + 1, &fdsr, 0, 0, &tv);
if (s == -1) {
if (errno != EINTR)
ERR("can't select(): %s", strerror(errno));
}
else {
/* timeout */
if (s == 0)
update_text();
}
}
if (need_to_update) {
#ifdef OWN_WINDOW
int wx = window.x, wy = window.y;
#endif
need_to_update = 0;
update_text_area();
#ifdef OWN_WINDOW
if (own_window) {
/* resize window if it isn't right size */
if (!fixed_size &&
(text_width+border_margin*2 != window.width ||
text_height+border_margin*2 != window.height)) {
window.width = text_width + border_margin*2 + 1;
window.height = text_height + border_margin*2 + 1;
XResizeWindow(display, window.window, window.width, window.height);
}
/* move window if it isn't in right position */
if (!fixed_pos &&
(window.x != wx || window.y != wy)) {
XMoveWindow(display, window.window, window.x, window.y);
}
}
#endif
clear_text(1);
#ifdef XDBE
if (use_xdbe) {
XRectangle r;
r.x = text_start_x - border_margin;
r.y = text_start_y - border_margin;
r.width = text_width + border_margin*2;
r.height = text_height + border_margin*2;
XUnionRectWithRegion(&r, region, region);
}
#endif
}
/* handle X events */
while (XPending(display)) {
XEvent ev;
XNextEvent(display, &ev);
switch (ev.type) {
case Expose: {
XRectangle r;
r.x = ev.xexpose.x;
r.y = ev.xexpose.y;
r.width = ev.xexpose.width;
r.height = ev.xexpose.height;
XUnionRectWithRegion(&r, region, region);
}
break;
#ifdef OWN_WINDOW
case ReparentNotify:
/* set background to ParentRelative for all parents */
if (own_window)
set_transparent_background(window.window);
break;
case ConfigureNotify:
if (own_window) {
/* if window size isn't what expected, set fixed size */
if (ev.xconfigure.width != window.width ||
ev.xconfigure.height != window.height) {
if (window.width != 0 && window.height != 0)
fixed_size = 1;
/* clear old stuff before screwing up size and pos */
clear_text(1);
{
XWindowAttributes attrs;
if (XGetWindowAttributes(display, window.window, &attrs)) {
window.width = attrs.width;
window.height = attrs.height;
}
}
text_width = window.width - border_margin*2 - 1;
text_height = window.height - border_margin*2 - 1;
}
/* if position isn't what expected, set fixed pos, total_updates
* avoids setting fixed_pos when window is set to weird locations
* when started */
if (total_updates >= 2 && !fixed_pos &&
(window.x != ev.xconfigure.x || window.y != ev.xconfigure.y) &&
(ev.xconfigure.x != 0 || ev.xconfigure.y != 0)) {
fixed_pos = 1;
}
}
break;
#endif
default:
break;
}
}
/* XDBE doesn't seem to provide a way to clear the back buffer without
* interfering with the front buffer, other than passing XdbeBackground
* to XdbeSwapBuffers. That means that if we're using XDBE, we need to
* redraw the text even if it wasn't part of the exposed area. OTOH,
* if we're not going to call draw_stuff at all, then no swap happens
* and we can safely do nothing.
*/
if (!XEmptyRegion(region)) {
#ifdef XDBE
if (use_xdbe) {
XRectangle r;
r.x = text_start_x - border_margin;
r.y = text_start_y - border_margin;
r.width = text_width + border_margin*2;
r.height = text_height + border_margin*2;
XUnionRectWithRegion(&r, region, region);
}
#endif
XSetRegion(display, window.gc, region);
#ifdef XFT
if (use_xft)
XftDrawSetClip(window.xftdraw, region);
#endif
draw_stuff();
XDestroyRegion(region);
region = XCreateRegion();
}
}
}
static void load_font() {
#ifdef XFT
/* load Xft font */
if (use_xft) {
if (xftfont != NULL) XftFontClose(display, xftfont);
if ((xftfont = XftFontOpenName(display, screen, font_name)) != NULL)
return;
ERR("can't load Xft font '%s'", font_name);
if ((xftfont = XftFontOpenName(display, screen, "courier-12")) != NULL)
return;
ERR("can't load Xft font '%s'", "courier-12");
if ((font = XLoadQueryFont(display, "fixed")) == NULL) {
CRIT_ERR("can't load font '%s'", "fixed");
}
use_xft = 0;
return;
}
#endif
/* load normal font */
if (font != NULL) XFreeFont(display, font);
if ((font = XLoadQueryFont(display, font_name)) == NULL) {
ERR("can't load font '%s'", font_name);
if ((font = XLoadQueryFont(display, "fixed")) == NULL) {
CRIT_ERR("can't load font '%s'", "fixed");
}
}
}
static void set_font() {
#ifdef XFT
if (use_xft) {
if (window.xftdraw != NULL) XftDrawDestroy(window.xftdraw);
window.xftdraw = XftDrawCreate(display, window.drawable,
DefaultVisual(display, screen), DefaultColormap(display, screen));
}
else
#endif
{
XSetFont(display, window.gc, font->fid);
}
}
static void load_config_file(const char *);
/* signal handler that reloads config file */
static void reload_handler(int a) {
fprintf(stderr, "torsmo: received signal %d, reloading config\n", a);
if (current_config) {
clear_fs_stats();
load_config_file(current_config);
load_font();
set_font();
extract_variable_text(text);
free(text);
text = NULL;
update_text();
}
}
static void clean_up() {
#ifdef XDBE
if (use_xdbe)
XdbeDeallocateBackBufferName(display, window.back_buffer);
#endif
#ifdef OWN_WINDOW
if (own_window)
XDestroyWindow(display, window.window);
else
#endif
{
clear_text(1);
XFlush(display);
}
XFreeGC(display, window.gc);
/* it is really pointless to free() memory at the end of program but ak|ra
* wants me to do this */
free_text_objects();
if (text != original_text)
free(text);
free(current_config);
free(current_mail_spool);
#ifdef SETI
free(seti_dir);
#endif
}
static void term_handler(int a) {
a = a; /* to get rid of warning */
clean_up();
exit(0);
}
static int string_to_bool(const char *s) {
if (!s) return 1;
if (strcasecmp(s, "yes") == 0) return 1;
if (strcasecmp(s, "true") == 0) return 1;
if (strcasecmp(s, "1") == 0) return 1;
return 0;
}
static enum alignment string_to_alignment(const char *s) {
if (strcasecmp(s, "top_left") == 0) return TOP_LEFT;
else if (strcasecmp(s, "top_right") == 0) return TOP_RIGHT;
else if (strcasecmp(s, "bottom_left") == 0) return BOTTOM_LEFT;
else if (strcasecmp(s, "bottom_right") == 0) return BOTTOM_RIGHT;
else if (strcasecmp(s, "tl") == 0) return TOP_LEFT;
else if (strcasecmp(s, "tr") == 0) return TOP_RIGHT;
else if (strcasecmp(s, "bl") == 0) return BOTTOM_LEFT;
else if (strcasecmp(s, "br") == 0) return BOTTOM_RIGHT;
return TOP_LEFT;
}
static void set_default_configurations(void) {
text_alignment = BOTTOM_LEFT;
fork_to_background = 0;
border_margin = 3;
border_width = 1;
default_fg_color = WhitePixel(display, screen);
default_bg_color = BlackPixel(display, screen);
default_out_color = BlackPixel(display, screen);
draw_borders = 0;
draw_shades = 1;
draw_outline = 0;
free(font_name);
#ifdef XFT
use_xft = 1;
font_name = strdup("courier-12");
#else
font_name = strdup("6x10");
#endif
gap_x = 5;
gap_y = 5;
free(current_mail_spool);
{
char buf[256];
variable_substitute(MAIL_FILE, buf, 256);
if (buf[0] != '\0')
current_mail_spool = strdup(buf);
}
minimum_width = 5;
minimum_height = 5;
no_buffers = 1;
#ifdef OWN_WINDOW
own_window = 0;
#endif
stippled_borders = 0;
update_interval = 10.0;
stuff_in_upper_case = 0;
}
static void load_config_file(const char *f) {
#define CONF_ERR ERR("%s: %d: config file error", f, line);
int line = 0;
FILE *fp;
set_default_configurations();
fp = open_file(f, 0);
if (!fp) return;
while (!feof(fp)) {
char buf[256], *p, *p2, *name, *value;
line++;
if (fgets(buf, 256, fp) == NULL) break;
p = buf;
/* break at comment */
p2 = strchr(p, '#');
if (p2) *p2 = '\0';
/* skip spaces */
while (*p && isspace((int) *p)) p++;
if (*p == '\0') continue; /* empty line */
name = p;
/* skip name */
p2 = p;
while (*p2 && !isspace((int) *p2)) p2++;
if (*p2 != '\0') {
*p2 = '\0'; /* break at name's end */
p2++;
}
/* get value */
if (*p2) {
p = p2;
while (*p && isspace((int) *p)) p++;
value = p;
p2 = value + strlen(value);
while (isspace((int) *(p2-1))) *--p2 = '\0';
}
else {
value = 0;
}
#define CONF2(a) if (strcasecmp(name, a) == 0)
#define CONF(a) else CONF2(a)
#define CONF3(a,b) \
else if (strcasecmp(name, a) == 0 || strcasecmp(name, a) == 0)
CONF2("alignment") {
if (value) {
int a = string_to_alignment(value);
if (a <= 0)
CONF_ERR
else
text_alignment = a;
}
else
CONF_ERR
}
CONF("background") {
fork_to_background = string_to_bool(value);
}
CONF("border_margin") {
if(value)
border_margin = strtol(value, 0, 0);
else
CONF_ERR
}
CONF("border_width") {
if(value)
border_width = strtol(value, 0, 0);
else
CONF_ERR
}
CONF("default_color") {
if (value)
default_fg_color = get_x11_color(value);
else
CONF_ERR
}
CONF3("default_shade_color", "default_shadecolor") {
if (value)
default_bg_color = get_x11_color(value);
else
CONF_ERR
}
CONF3("default_outline_color", "default_outlinecolor") {
if (value)
default_out_color = get_x11_color(value);
else
CONF_ERR
}
#ifdef XDBE
CONF("double_buffer") {
use_xdbe = string_to_bool(value);
}
#endif
CONF("draw_borders") {
draw_borders = string_to_bool(value);
}
CONF("draw_shades") {
draw_shades = string_to_bool(value);
}
CONF("draw_outline") {
draw_outline = string_to_bool(value);
}
#ifdef XFT
CONF("use_xft") {
use_xft = string_to_bool(value);
}
CONF("font") {
/* font silently ignored when Xft */
}
CONF("xftalpha") {
if (value)
font_alpha = atof(value) * 65535.0;
else
CONF_ERR
}
CONF("xftfont") {
#else
CONF("use_xft") {
if(string_to_bool(value))
ERR("Xft not enabled");
}
CONF("xftfont") {
/* xftfont silently ignored when no Xft */
}
CONF("xftalpha") {
/* xftalpha is silently ignored when no Xft */
}
CONF("font") {
#endif
if (value) {
free(font_name);
font_name = strdup(value);
}
else
CONF_ERR
}
CONF("gap_x") {
if (value)
gap_x = atoi(value);
else
CONF_ERR
}
CONF("gap_y") {
if (value)
gap_y = atoi(value);
else
CONF_ERR
}
CONF("mail_spool") {
if (value) {
char buf[256];
variable_substitute(value, buf, 256);
if (buf[0] != '\0') {
if (current_mail_spool) free(current_mail_spool);
current_mail_spool = strdup(buf);
}
}
else
CONF_ERR
}
CONF("minimum_size") {
if (value) {
if (sscanf(value, "%d %d", &minimum_width, &minimum_height) != 2)
if (sscanf(value, "%d", &minimum_width) != 1)
CONF_ERR
}
else
CONF_ERR
}
CONF("no_buffers") {
no_buffers = string_to_bool(value);
}
#ifdef OWN_WINDOW
CONF("own_window") {
own_window = string_to_bool(value);
}
#endif
CONF("pad_percents") {
pad_percents = atoi(value);
}
CONF("stippled_borders") {
if(value)
stippled_borders = strtol(value, 0, 0);
else
stippled_borders = 4;
}
CONF("temp1") {
ERR("temp1 configuration is obsolete, use ${i2c <i2c device here> temp 1}");
}
CONF("temp1") {
ERR("temp2 configuration is obsolete, use ${i2c <i2c device here> temp 2}");
}
CONF("update_interval") {
if (value)
update_interval = strtod(value, 0);
else
CONF_ERR
}
CONF("uppercase") {
stuff_in_upper_case = string_to_bool(value);
}
#ifdef SETI
CONF("seti_dir") {
seti_dir = (char *)malloc(strlen(value) + 1);
strcpy(seti_dir, value);
}
#endif
CONF("text") {
if (text != original_text)
free(text);
text = (char *) malloc(1);
text[0] = '\0';
while (!feof(fp)) {
unsigned int l = strlen(text);
if (fgets(buf, 256, fp) == NULL) break;
text = (char *) realloc(text, l + strlen(buf) + 1);
strcat(text, buf);
if (strlen(text) > 1024*8) break;
}
fclose(fp);
return;
}
else
ERR("%s: %d: no such configuration: '%s'", f, line, name);
#undef CONF
#undef CONF2
}
fclose(fp);
#undef CONF_ERR
}
/* : means that character before that takes an argument */
static const char *getopt_string = "vVdt:f:u:hc:w:x:y:a:"
#ifdef OWN_WINDOW
"o"
#endif
#ifdef XDBE
"b"
#endif
;
int main(int argc, char **argv) {
/* handle command line parameters that don't change configs */
while (1) {
int c = getopt(argc, argv, getopt_string);
if (c == -1) break;
switch (c) {
case 'v':
case 'V':
printf("torsmo " VERSION " compiled " __DATE__ "\n");
return 0;
case 'c':
/* if current_config is set to a strdup of CONFIG_FILE, free it (even
* though free() does the NULL check itself;), then load optarg value */
if (current_config) free(current_config);
current_config = strdup(optarg);
break;
case 'h':
printf(
"Usage: %s [OPTION]...\n"
"Torsmo is a system monitor that renders text on desktop or to own transparent\n"
"window. Command line options will override configurations defined in config\n"
"file.\n"
" -V version\n"
" -a ALIGNMENT text alignment on screen, {top,bottom}_{left,right}\n"
" -c FILE config file to load instead of " CONFIG_FILE "\n"
" -d daemonize, fork to background\n"
" -f FONT font to use\n"
" -h help\n"
#ifdef OWN_WINDOW
" -o create own window to draw\n"
#endif
#ifdef XDBE
" -b double buffer (prevents flickering)\n"
#endif
" -t TEXT text to render, remember single quotes, like -t '$uptime'\n"
" -u SECS update interval\n"
" -w WIN_ID window id to draw\n"
" -x X x position\n"
" -y Y y position\n"
, argv[0]);
return 0;
case 'w':
window.window = strtol(optarg, 0, 0);
break;
case '?':
exit(EXIT_FAILURE);
}
}
/* initalize X BEFORE we load config. (we need to so that 'screen' is set) */
init_X11();
/* load current_config or CONFIG_FILE */
#ifdef CONFIG_FILE
if (current_config == NULL)
{
/* load default config file */
char buf[256];
variable_substitute(CONFIG_FILE, buf, 256);
if (buf[0] != '\0')
current_config = strdup(buf);
}
#endif
if (current_config != NULL)
load_config_file(current_config);
else
set_default_configurations();
#ifdef MAIL_FILE
if (current_mail_spool == NULL) {
char buf[256];
variable_substitute(MAIL_FILE, buf, 256);
if (buf[0] != '\0')
current_mail_spool = strdup(buf);
}
#endif
/* handle other command line arguments */
optind = optreset = 1;
while (1) {
int c = getopt(argc, argv, getopt_string);
if(c == -1) break;
switch (c) {
case 'a':
text_alignment = string_to_alignment(optarg);
break;
case 'd':
fork_to_background = 1;
break;
case 'f':
font_name = strdup(optarg);
break;
#ifdef OWN_WINDOW
case 'o':
own_window = 1;
break;
#endif
#ifdef XDBE
case 'b':
use_xdbe = 1;
break;
#endif
case 't':
if (text != original_text) free(text);
text = strdup(optarg);
convert_escapes(text);
break;
case 'u':
update_interval = strtod(optarg, 0);
break;
case 'x':
gap_x = atoi( optarg );
break;
case 'y':
gap_y = atoi( optarg );
break;
case '?':
exit(EXIT_FAILURE);
}
}
/* load font */
load_font();
/* generate text and get initial size */
extract_variable_text(text);
if (text != original_text) free(text);
text = NULL;
update_uname();
generate_text();
update_text_area(); /* to get initial size of the window */
init_window(own_window,
text_width + border_margin*2 + 1,
text_height + border_margin*2 + 1);
update_text_area(); /* to position text/window on screen */
#ifdef OWN_WINDOW
if(own_window)
XMoveWindow(display, window.window, window.x, window.y);
#endif
create_gc();
set_font();
draw_stuff();
/* fork */
if (fork_to_background) {
int ret = fork();
switch (ret) {
case -1:
ERR("can't fork() to background: %s", strerror(errno));
break;
case 0:
break;
default:
fprintf(stderr, "torsmo: forked to background, pid is %d\n", ret);
return 0;
}
}
/* set SIGUSR1, SIGINT and SIGTERM handlers */
{
struct sigaction sa;
sa.sa_handler = reload_handler;
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART;
if (sigaction(SIGUSR1, &sa, NULL) != 0)
ERR("can't set signal handler for SIGUSR1: %s", strerror(errno));
sa.sa_handler = term_handler;
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART;
if (sigaction(SIGINT, &sa, NULL) != 0)
ERR("can't set signal handler for SIGINT: %s", strerror(errno));
sa.sa_handler = term_handler;
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART;
if (sigaction(SIGTERM, &sa, NULL) != 0)
ERR("can't set signal handler for SIGTERM: %s", strerror(errno));
}
main_loop();
return 0;
}
syntax highlighted by Code2HTML, v. 0.9.1