diff --git a/Makefile b/Makefile index 3744ef6..0f79ef1 100644 --- a/Makefile +++ b/Makefile @@ -8,7 +8,8 @@ clean: rm -f paleofetch $(CACHE)/paleofetch paleofetch: paleofetch.c paleofetch.h config.h - $(CC) paleofetch.c -o paleofetch $(CFLAGS) + $(eval battery_path := $(shell ./config_scripts/battery_config.sh)) + $(CC) paleofetch.c -o paleofetch $(CFLAGS) -D $(battery_path) strip paleofetch install: paleofetch diff --git a/README.md b/README.md index eb81bb9..c966979 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,14 @@ paleofetch A rewrite of [neofetch](https://github.com/dylanaraps/neofetch) in C. Currently only supports Linux and Xorg. + +Why use paleofetch over neofetch? +----------------------------------------- +One major reason is the performance improvement. For example: neofetch finishes running after about 222 milliseconds where as paleofetch can finish running in a blazing fast 3 milliseconds. + +Note: this testing occured on only 1 computer, it's not a good representation on the performance benefit you may gain. + + Example output: ![example output](example.png) @@ -77,6 +85,15 @@ The booleans in `CONFIG` tell paleofetch whether you want to cache an entry. When cached, paleofetch will save the value and not recompute it whenever you run paleofetch (unless you specify the `--recache` option). +The CPU and GPU name can be configured as well. This is done under the CPU_CONFIG and GPU_CONFIG section +in the config.h file. Two macros are provided to customize and tidy up the model names: + +* `REMOVE(string)`: removes the first occurence of `string` +* `REPLACE(string1, string2)`: replaces the first occurence of `string1` with `string2` + +Don't forget to run paleofetch with the --recache flag after compiling it with your new +configuration, otherwise it will still show the old name for already cached entries. + FAQ --- diff --git a/config.h b/config.h index db8fecf..39c621c 100644 --- a/config.h +++ b/config.h @@ -3,28 +3,29 @@ #define CONFIG \ { \ - /* name function cached */\ - { "", get_title, false }, \ - { "", get_bar, false }, \ - { "OS: ", get_os, true }, \ - { "Host: ", get_host, true }, \ - { "Kernel: ", get_kernel, true }, \ - { "Uptime: ", get_uptime, false }, \ + /* name function cached */\ + { "", get_title, false }, \ + { "", get_bar, false }, \ + { "OS: ", get_os, true }, \ + { "Host: ", get_host, true }, \ + { "Kernel: ", get_kernel, true }, \ + { "Uptime: ", get_uptime, false }, \ + { "Battery: ", get_battery_percentage, false }, \ SPACER \ { "Packages: ", get_packages_pacman, false }, \ { "Shell: ", get_shell, false }, \ { "Resolution: ", get_resolution, false }, \ { "Terminal: ", get_terminal, false }, \ SPACER \ - { "CPU: ", get_cpu, true }, \ - { "GPU: ", get_gpu1, true }, \ - { "Memory: ", get_memory, false }, \ + { "CPU: ", get_cpu, true }, \ + { "GPU: ", get_gpu1, true }, \ + { "Memory: ", get_memory, false }, \ SPACER \ - { "", get_colors1, false }, \ - { "", get_colors2, false }, \ + { "", get_colors1, false }, \ + { "", get_colors2, false }, \ } -#define CPU_REMOVE \ +#define CPU_CONFIG \ { \ REMOVE("(R)"), \ REMOVE("(TM)"), \ @@ -36,7 +37,7 @@ REMOVE("CPU"), \ } -#define GPU_REMOVE \ +#define GPU_CONFIG \ { \ REMOVE("Corporation"), \ } diff --git a/config_scripts/battery_config.sh b/config_scripts/battery_config.sh new file mode 100755 index 0000000..0a49740 --- /dev/null +++ b/config_scripts/battery_config.sh @@ -0,0 +1,5 @@ +#!/bin/sh + +BATTERY_DIRECTORY=`ls /sys/class/power_supply | grep -i "^bat" | head -n 1` + +echo "BATTERY_DIRECTORY='\"/sys/class/power_supply/$BATTERY_DIRECTORY\"'" diff --git a/paleofetch.c b/paleofetch.c index a6edbbe..72ce826 100644 --- a/paleofetch.c +++ b/paleofetch.c @@ -37,8 +37,10 @@ struct conf { struct { char *substring; + char *repl_str; size_t length; -} cpu_remove[] = CPU_REMOVE, gpu_remove[] = GPU_REMOVE; + size_t repl_len; +} cpu_config[] = CPU_CONFIG, gpu_config[] = GPU_CONFIG; Display *display; struct statvfs file_stats; @@ -55,6 +57,17 @@ void remove_newline(char *s) { *s = '\0'; } +/* + * Replaces the first newline character with null terminator + * and returns the length of the string + */ +int remove_newline_get_length(char *s) { + int i; + for (i = 0; *s != '\0' && *s != '\n'; s++, i++); + *s = '\0'; + return i; +} + /* * Cleans up repeated spaces in a string * Trim spaces at the front of a string @@ -89,6 +102,26 @@ void remove_substring(char *str, const char* substring, size_t len) { while(*(sub+(++i)) != '\0'); } +/* + * Replaces the first sub_len characters of sub_str from str + * with the first repl_len characters of repl_str + */ +void replace_substring(char *str, const char *sub_str, const char *repl_str, size_t sub_len, size_t repl_len) { + char buffer[BUF_SIZE / 2]; + char *start = strstr(str, sub_str); + if (start == NULL) return; // substring not found + + /* check if we have enough space for new substring */ + if (strlen(str) - sub_len + repl_len >= BUF_SIZE / 2) { + status = -1; + halt_and_catch_fire("new substring too long to replace"); + } + + strcpy(buffer, start + sub_len); + strncpy(start, repl_str, repl_len); + strcpy(start + repl_len, buffer); +} + static char *get_title() { // reduce the maximum size for these, so that we don't over-fill the title string char hostname[BUF_SIZE / 3]; @@ -145,36 +178,36 @@ static char *get_kernel() { } static char *get_host() { - FILE *product_name = fopen("/sys/devices/virtual/dmi/id/product_name", "r"); - - if(product_name == NULL) { - status = -1; - halt_and_catch_fire("unable to open product name file"); + char *host = malloc(BUF_SIZE), buffer[BUF_SIZE/2]; + FILE *product_name, *product_version, *model; + + if((product_name = fopen("/sys/devices/virtual/dmi/id/product_name", "r")) != NULL) { + if((product_version = fopen("/sys/devices/virtual/dmi/id/product_version", "r")) != NULL) { + fread(host, 1, BUF_SIZE/2, product_name); + remove_newline(host); + strcat(host, " "); + fread(buffer, 1, BUF_SIZE/2, product_version); + remove_newline(buffer); + strcat(host, buffer); + fclose(product_version); + } else { + fclose(product_name); + goto model_fallback; + } + fclose(product_name); + return host; } - char *host = malloc(BUF_SIZE); - fread(host, 1, BUF_SIZE, product_name); - fclose(product_name); - - FILE *product_version = fopen("/sys/devices/virtual/dmi/id/product_version", "r"); - - if(product_version == NULL) { - status = -1; - halt_and_catch_fire("unable to open product version file"); +model_fallback: + if((model = fopen("/sys/firmware/devicetree/base/model", "r")) != NULL) { + fread(host, 1, BUF_SIZE, model); + remove_newline(host); + return host; } - char version[BUF_SIZE]; - - fread(version, 1, BUF_SIZE, product_version); - fclose(product_version); - - remove_newline(host); - remove_newline(version); - - strcat(host, " "); - strcat(host, version); - - return host; + status = -1; + halt_and_catch_fire("unable to get host"); + return NULL; } static char *get_uptime() { @@ -199,6 +232,37 @@ static char *get_uptime() { return uptime; } +// returns "% []" +// Credit: allisio - https://gist.github.com/allisio/1e850b93c81150124c2634716fbc4815 +static char *get_battery_percentage() { + int battery_capacity; + FILE *capacity_file, *status_file; + char battery_status[12] = "Unknown"; + + if ((capacity_file = fopen(BATTERY_DIRECTORY "/capacity", "r")) == NULL) { + status = ENOENT; + halt_and_catch_fire("Unable to get battery information"); + } + + fscanf(capacity_file, "%d", &battery_capacity); + fclose(capacity_file); + + if ((status_file = fopen(BATTERY_DIRECTORY "/status", "r")) != NULL) { + fscanf(status_file, "%s", battery_status); + fclose(status_file); + } + + // max length of resulting string is 19 + // one byte for padding incase there is a newline + // 100% [Discharging] + // 1234567890123456789 + char *battery = malloc(20); + + snprintf(battery, 20, "%d%% [%s]", battery_capacity, battery_status); + + return battery; +} + static char *get_packages(const char* dirname, const char* pacname, int num_extraneous) { int num_packages = 0; DIR * dirp; @@ -326,6 +390,10 @@ static char *get_terminal() { } else { terminal_fallback: strncpy(terminal, getenv("TERM"), BUF_SIZE); /* fallback to old method */ + /* in tty, $TERM is simply returned as "linux"; in this case get actual tty name */ + if (strcmp(terminal, "linux") == 0) { + strncpy(terminal, ttyname(STDIN_FILENO), BUF_SIZE); + } } return terminal; @@ -343,6 +411,7 @@ static char *get_cpu() { size_t len; /* unused */ int num_cores = 0, cpu_freq, prec = 3; double freq; + char freq_unit[] = "GHz"; /* read the model name into cpu_model, and increment num_cores every time model name is found */ while(getline(&line, &len, cpuinfo) != -1) { @@ -352,40 +421,67 @@ static char *get_cpu() { fclose(cpuinfo); FILE *cpufreq = fopen("/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq", "r"); + line = NULL; - if (cpufreq == NULL) { - status = -1; - halt_and_catch_fire("Unable to open cpufreq"); + if (cpufreq != NULL) { + if (getline(&line, &len, cpufreq) != -1) { + sscanf(line, "%d", &cpu_freq); + cpu_freq /= 1000; // convert kHz to MHz + } else { + fclose(cpufreq); + free(line); + goto cpufreq_fallback; + } + } else { +cpufreq_fallback: + cpufreq = fopen("/proc/cpuinfo", "r"); /* read from cpu info */ + if (cpufreq == NULL) { + status = -1; + halt_and_catch_fire("Unable to open cpuinfo"); + } + + while (getline(&line, &len, cpufreq) != -1) { + if (sscanf(line, "cpu MHz : %lf", &freq) > 0) break; + } + + cpu_freq = (int) freq; } - line = NULL; + free(line); + fclose(cpufreq); - if (getline(&line, &len, cpufreq) != -1) { - sscanf(line, "%d", &cpu_freq); - cpu_freq /= 1000; // convert kHz to MHz + if (cpu_freq < 1000) { + freq = (double) cpu_freq; + freq_unit[0] = 'M'; // make MHz from GHz + prec = 0; // show frequency as integer value + } else { freq = cpu_freq / 1000.0; // convert MHz to GHz and cast to double + while (cpu_freq % 10 == 0) { --prec; cpu_freq /= 10; } - if (prec == 0) prec = 1; // we don't want zero decimal places - } else { - freq = 0.0; // cpuinfo_max_freq not available? - } - free(line); - fclose(cpufreq); + if (prec == 0) prec = 1; // we don't want zero decimal places + } /* remove unneeded information */ - for (int i = 0; i < COUNT(cpu_remove); ++i) { - remove_substring(cpu_model, cpu_remove[i].substring, cpu_remove[i].length); + for (int i = 0; i < COUNT(cpu_config); ++i) { + if (cpu_config[i].repl_str == NULL) { + remove_substring(cpu_model, cpu_config[i].substring, cpu_config[i].length); + } else { + replace_substring(cpu_model, cpu_config[i].substring, cpu_config[i].repl_str, cpu_config[i].length, cpu_config[i].repl_len); + } } char *cpu = malloc(BUF_SIZE); - snprintf(cpu, BUF_SIZE, "%s (%d) @ %.*fGHz", cpu_model, num_cores, prec, freq); + snprintf(cpu, BUF_SIZE, "%s (%d) @ %.*f%s", cpu_model, num_cores, prec, freq, freq_unit); free(cpu_model); truncate_spaces(cpu); + + if(num_cores == 0) + *cpu = '\0'; return cpu; } @@ -424,8 +520,12 @@ static char *find_gpu(int index) { pci_cleanup(pacc); /* remove unneeded information */ - for (int i = 0; i < COUNT(gpu_remove); ++i) { - remove_substring(gpu, gpu_remove[i].substring, gpu_remove[i].length); + for (int i = 0; i < COUNT(gpu_config); ++i) { + if (gpu_config[i].repl_str == NULL) { + remove_substring(gpu, gpu_config[i].substring, gpu_config[i].length); + } else { + replace_substring(gpu, gpu_config[i].substring, gpu_config[i].repl_str, gpu_config[i].length, gpu_config[i].repl_len); + } } truncate_spaces(gpu); diff --git a/paleofetch.h b/paleofetch.h index c438eb2..0e4578f 100644 --- a/paleofetch.h +++ b/paleofetch.h @@ -7,6 +7,7 @@ static char *get_title(), *get_kernel(), *get_host(), *get_uptime(), + *get_battery_percentage(), *get_packages_pacman(), *get_shell(), *get_resolution(), @@ -22,4 +23,5 @@ static char *get_title(), *spacer(); #define SPACER {"", spacer, false}, -#define REMOVE(A) { (A), sizeof(A) - 1} +#define REMOVE(A) { (A), NULL, sizeof(A) - 1 , 0 } +#define REPLACE(A, B) { (A), (B), sizeof(A) - 1, sizeof(B) - 1 }