diff options
| author | Michael Stapelberg <michael@stapelberg.de> | 2011-07-13 03:27:57 +0200 | 
|---|---|---|
| committer | Michael Stapelberg <michael@stapelberg.de> | 2011-07-13 03:27:57 +0200 | 
| commit | 7ab9167536e4ca0ccb5c5a797e693c484e0456d6 (patch) | |
| tree | 26a60cb557cb4719dc429e3c157dd8790fa76451 | |
| parent | a46984d06ccc234d07ced0bda80fa8d6f3f21076 (diff) | |
magic: try to auto-detect output_format by default (dzen2/i3bar/xmobar)
| -rw-r--r-- | i3status.c | 13 | ||||
| -rw-r--r-- | include/i3status.h | 3 | ||||
| -rw-r--r-- | src/auto_detect_format.c | 142 | 
3 files changed, 157 insertions, 1 deletions
| @@ -173,7 +173,7 @@ int main(int argc, char *argv[]) {          unsigned int j;          cfg_opt_t general_opts[] = { -                CFG_STR("output_format", "dzen2", CFGF_NONE), +                CFG_STR("output_format", "auto", CFGF_NONE),                  CFG_BOOL("colors", 1, CFGF_NONE),                  CFG_STR("color_good", "#00FF00", CFGF_NONE),                  CFG_STR("color_degraded", "#FFFF00", CFGF_NONE), @@ -312,6 +312,17 @@ int main(int argc, char *argv[]) {                  die("Could not get section \"general\"\n");          char *output_str = cfg_getstr(cfg_general, "output_format"); +        if (strcasecmp(output_str, "auto") == 0) { +                fprintf(stderr, "i3status: trying to auto-detect output_format setting\n"); +                output_str = auto_detect_format(); +                if (!output_str) { +                        output_str = "none"; +                        fprintf(stderr, "i3status: falling back to \"none\"\n"); +                } else { +                        fprintf(stderr, "i3status: auto-detected \"%s\"\n", output_str); +                } +        } +          if (strcasecmp(output_str, "dzen2") == 0)                  output_format = O_DZEN2;          else if (strcasecmp(output_str, "xmobar") == 0) diff --git a/include/i3status.h b/include/i3status.h index ae1983c..125ca55 100644 --- a/include/i3status.h +++ b/include/i3status.h @@ -59,6 +59,9 @@ void print_seperator();  char *color(const char *colorstr);  char *endcolor() __attribute__ ((pure)); +/* src/auto_detect_format.c */ +char *auto_detect_format(); +  void print_ipv6_info(const char *format_up, const char *format_down);  void print_disk_info(const char *path, const char *format);  void print_battery_info(int number, const char *format, bool last_full_capacity); diff --git a/src/auto_detect_format.c b/src/auto_detect_format.c new file mode 100644 index 0000000..b8dfdfc --- /dev/null +++ b/src/auto_detect_format.c @@ -0,0 +1,142 @@ +/* + * vim:ts=4:sw=4:expandtab + */ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <stdbool.h> +#include <stdint.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <dirent.h> + +#include "i3status.h" + +/* + * This function tries to automatically find out where i3status is being piped + * to and choses the appropriate output format. + * + * It is a little hackish but should work for most setups :). + * + * By iterating through /proc/<number>/stat and finding out the parent process + * id (just like pstree(1) or ps(1) work), we can get all children of our + * parent. When the output of i3status is being piped somewhere, the shell + * (parent process) spawns i3status and the destination process, so we will + * find our own process and the pipe target. + * + * We then check whether the pipe target’s name is known and chose the format. + * + */ +char *auto_detect_format() { +    pid_t myppid = getppid(); +    pid_t mypid = getpid(); + +    DIR *dir; +    struct dirent *entry; +    char path[255]; +    /* the relevant contents (for us) are: +     * <pid> (<program name>) <status> <ppid> +     * which should well fit into one page of 4096 bytes */ +    char buffer[4096]; + +    char *format = NULL; + +    char *parentname = NULL; + +    if (!(dir = opendir("/proc"))) +        return NULL; + +    /* First pass: get the executable name of the parent. +     * Upon error, we directly return NULL as we cannot continue without the +     * name of our parent process. */ +    while ((entry = readdir(dir)) != NULL) { +        pid_t pid = (pid_t)atoi(entry->d_name); +        if (pid != myppid) +            continue; + +        if (snprintf(path, sizeof(path), "/proc/%d/stat", pid) == -1 || +            !slurp(path, buffer, 4095)) +            goto out; + +        buffer[4095] = '\0'; +        char *leftbracket = strchr(buffer, '('); +        char *rightbracket = strrchr(buffer, ')'); +        if (!leftbracket || +            !rightbracket || +            !(parentname = malloc((rightbracket - leftbracket)))) +            goto out; +        *rightbracket = '\0'; +        strcpy(parentname, leftbracket + 1); +    } + +    if (!parentname) +        goto out; + +    rewinddir(dir); + +    while ((entry = readdir(dir)) != NULL) { +        pid_t pid = (pid_t)atoi(entry->d_name); +        if (pid == 0 || pid == mypid) +            continue; + +        if (snprintf(path, sizeof(path), "/proc/%d/stat", pid) == -1) +            continue; + +        char *name = NULL; +        pid_t ppid; +        int loopcnt = 0; +        /* Now we need to find out the name of the process. +         * To avoid the possible race condition of the process existing already +         * but not executing the destination (shell after fork() and before +         * exec()), we check if the name equals its parent. +         * +         * We try this for up to 0.5 seconds, then we give up. +         */ +        do { +            /* give the scheduler a chance between each iteration, don’t hog +             * the CPU too much */ +            if (name) +                usleep(50); + +            if (!slurp(path, buffer, 4095)) +                break; +            buffer[4095] = '\0'; +            char *leftbracket = strchr(buffer, '('); +            char *rightbracket = strrchr(buffer, ')'); +            if (!leftbracket || +                !rightbracket || +                sscanf(rightbracket + 2, "%*c %d", &ppid) != 1 || +                ppid != myppid) +                break; +            *rightbracket = '\0'; +            name = leftbracket + 1; +        } while (strcmp(parentname, name) == 0 && loopcnt++ < 10000); + +        if (!name) +            continue; + +        /* Check for known destination programs and set format */ +        char *newfmt = NULL; +        if (strcasecmp(name, "i3bar") == 0) +            newfmt = "none"; +        else if (strcasecmp(name, "dzen2") == 0) +            newfmt = "dzen2"; +        else if (strcasecmp(name, "xmobar") == 0) +            newfmt = "xmobar"; + +        if (newfmt && format) { +            fprintf(stderr, "i3status: cannot auto-configure, situation ambiguous (format \"%s\" *and* \"%s\" detected)\n", newfmt, format); +            format = NULL; +            break; +        } else { +            format = newfmt; +        } +    } + +out: +    closedir(dir); + +    return format; +} | 
