/* torsmo, a system monitor * * This program is licensed under BSD license, read COPYING */ #include "torsmo.h" #include #include #include #include #include #include #include #include #include #include #include #if HAVE_DIRENT_H #include #endif #include #include #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= 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 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; itype) { 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 temp 1}"); } CONF("temp1") { ERR("temp2 configuration is obsolete, use ${i2c 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; }