From 93d48c140393f69271f0a672d68ae35d561ca428 Mon Sep 17 00:00:00 2001 From: Alastair Poole Date: Wed, 17 Oct 2018 10:39:58 +0100 Subject: [PATCH] evisum: add tingle command-line helper. --- NEWS | 3 + README.md | 17 +- makefile | 4 +- src/tingle/README | 47 ++ src/tingle/makefile | 23 + src/tingle/tingle.c | 1689 ++++++++++++++++++++++++++++++++++++++++++ src/tingle/tmux.conf | 124 ++++ src/tingle/volctl | 142 ++++ 8 files changed, 2042 insertions(+), 7 deletions(-) create mode 100644 src/tingle/README create mode 100644 src/tingle/makefile create mode 100644 src/tingle/tingle.c create mode 100644 src/tingle/tmux.conf create mode 100755 src/tingle/volctl diff --git a/NEWS b/NEWS index 4f1ef14..6873af3 100644 --- a/NEWS +++ b/NEWS @@ -2,6 +2,9 @@ Evisum 0.2.0 ============ + * Add command-line utility (tingle) to complement + evisum. + * Fix object destruction befor exitting main loop. * Add variable unit display (K, M and G). * Minor refactoring. diff --git a/README.md b/README.md index 855bc8b..ce7eba4 100644 --- a/README.md +++ b/README.md @@ -2,13 +2,10 @@ System Information (EFL) -This is a process monitor and system monitor. +This is a process monitor and system monitor for Linux, macOS, +FreeBSD, DragonFlyBSD and OpenBSD. -Currently have full engines for Linux, FreeBSD, OpenBSD and MacOS. - -The plan is to rewrite the whole program to log system information -over time to disk so we can do nice things with E and displaying -system information (not duplicating loads of code). +Includes 'tingle' a command-line utility for displaying sensor information. REQUIREMENTS: @@ -32,3 +29,11 @@ $ make PREFIX=/usr install or even: $ make PREFIX=/opt install + +NOTES + +The plan is to rewrite the whole program to log system information +over time to disk so we can do nice things with E and displaying +system information (not duplicating loads of code). + +For information on tingle, see src/tingle/README. diff --git a/makefile b/makefile index ebc0cd2..d4ec2fa 100644 --- a/makefile +++ b/makefile @@ -21,9 +21,10 @@ export LDFLAGS default: $(MAKE) -C src + $(MAKE) -C src/tingle clean: - $(MAKE) -C src clean + $(MAKE) -C src/tingle clean install: -mkdir -p $(PREFIX)/share/pixmaps @@ -32,3 +33,4 @@ install: install -m 0644 data/evisum.png $(PREFIX)/share/pixmaps install -m 0644 data/evisum.desktop $(PREFIX)/share/applications install -m 0755 evisum $(PREFIX)/bin + install -m 0755 src/tingle/tingle $(PREFIX)/bin diff --git a/src/tingle/README b/src/tingle/README new file mode 100644 index 0000000..f32a5f2 --- /dev/null +++ b/src/tingle/README @@ -0,0 +1,47 @@ +# UNIX sensors (tingle) + +# Query CPU, memory, network, power, audio and + temperature sensors. + +Supporting Linux, FreeBSD, OpenBSD, Mac OS and DragonFlyBSD. + +It's also probably a useful reference for querying common +system information on the most-common Unix-like operating +systems. + +This can be used with pipes by other processes to +query the hardware. Or just to display a status +line in screen or tmux or some Xorg program. + +If I'd thought more about it I'd have made it modular, so +apologies for the crust! + +Install: + make (or gmake) + +Usage: tingle [OPTIONS] + Where OPTIONS can be a combination of + -c + Show average CPU usage. + -C + Show all CPU cores and usage. + -k (KB) -m (MB) -g (GB) + Show memory usage (unit). + -n + Show network usage. + -p + Show power status (ac and battery percentage). + -t + Show temperature sensors (temperature in celcius). + -a + Display mixer values (system values). + -s + Show all in a nicely formatted status bar format. + This is the default behaviour with no arguments. + With other flags specify (in any order) which + components to display in the status bar. + -h | -help | --help + This help. + +Al Poole + diff --git a/src/tingle/makefile b/src/tingle/makefile new file mode 100644 index 0000000..ef6f905 --- /dev/null +++ b/src/tingle/makefile @@ -0,0 +1,23 @@ +PROGRAM=tingle +SOURCES=tingle.c +CFLAGS=-O2 -Wall -pedantic -std=c99 -lpthread +LDFLAGS=-lm +HAVE_ALSA := 0 + +ALSA_TEST := $(shell pkg-config --exists alsa 1>&2 2>/dev/null; echo $$?) +HAVE_ALSA := $(shell if [ $(ALSA_TEST) -eq 0 ]; then echo "true"; else echo "false"; fi) + +UNAME := $(shell uname -s) + +ifeq ($(UNAME),Darwin) + CFLAGS += -framework AudioToolBox +else ifeq ($(UNAME),Linux) + ifeq ($(HAVE_ALSA),true) + CFLAGS += -lasound -DHAVE_ALSA=1 + endif +endif + +default: + $(CC) $(CFLAGS) $(LDFLAGS) $(SOURCES) -o tingle +clean: + -rm $(PROGRAM) diff --git a/src/tingle/tingle.c b/src/tingle/tingle.c new file mode 100644 index 0000000..1fe1603 --- /dev/null +++ b/src/tingle/tingle.c @@ -0,0 +1,1689 @@ +/* + Copyright (c) 2017, Al Poole + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* Visit: http://haxlab.org */ +/* Build : cc -lm (file) -o (output) */ +/* What a shitstorm this turned into! hah! */ + +#define _DEFAULT_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(__APPLE__) && defined(__MACH__) +#define __MacOS__ +# include +# include +# include +# include +# include +# include +# include +#endif + +#if defined(__OpenBSD__) || defined(__NetBSD__) +# include +# include +# include +# include +# include +# include +#endif + +#if defined(__FreeBSD__) || defined(__DragonFly__) +# include +# include +# include +#endif + +#if defined(__linux__) +# include +#endif + +#if defined(__linux__) && defined(HAVE_ALSA) +# include +#endif + +#define CPU_STATES 5 + +#define MAX_BATTERIES 5 +#define INVALID_TEMP -999 + +/* Filter requests and results */ +#define RESULTS_CPU 0x01 +#define RESULTS_MEM 0x02 +#define RESULTS_PWR 0x04 +#define RESULTS_TMP 0x08 +#define RESULTS_AUD 0x10 +#define RESULTS_NET 0x20 +#define RESULTS_DEFAULT 0x3f +#define RESULTS_MEM_MB 0x40 +#define RESULTS_MEM_GB 0x80 +#define RESULTS_CPU_CORES 0x100 + +typedef struct +{ + float percent; + unsigned long total; + unsigned long idle; +} cpu_core_t; + +typedef struct +{ + unsigned long total; + unsigned long used; + unsigned long cached; + unsigned long buffered; + unsigned long shared; + unsigned long swap_total; + unsigned long swap_used; +} meminfo_t; + +typedef struct +{ + bool have_ac; + int battery_count; + + double charge_full; + double charge_current; + uint8_t percent; + + char battery_names[256]; + int *bat_mibs[MAX_BATTERIES]; + int ac_mibs[5]; +} power_t; + +typedef struct +{ + bool enabled; + uint8_t volume_left; + uint8_t volume_right; +} mixer_t; + +typedef struct results_t results_t; +struct results_t +{ + int cpu_count; + cpu_core_t **cores; + + meminfo_t memory; + + power_t power; + + mixer_t mixer; + + unsigned long incoming; + unsigned long outgoing; + + int temperature; +}; + +static void +_memsize_bytes_to_kb(unsigned long *bytes) +{ + *bytes = (unsigned int)*bytes >> 10; +} + +#define _memsize_kb_to_mb _memsize_bytes_to_kb + +static void +_memsize_kb_to_gb(unsigned long *bytes) +{ + *bytes = (unsigned int)*bytes >> 20; +} + +#if defined(__linux__) +static char * +Fcontents(const char *path) +{ + char *buf; + char byte[1]; + size_t count, bytes = 0; + FILE *f = fopen(path, "r"); + if (!f) return NULL; + + int n = 1024; + + buf = malloc(n * sizeof(byte) + 1); + if (!buf) exit(0 << 1); + + while ((count = (fread(byte, sizeof(byte), 1, f))) > 0) + { + bytes += sizeof(byte); + if (bytes == (n * sizeof(byte))) + { + n *= 2; + char *tmp = realloc(buf, n * sizeof(byte) + 1); + if (!tmp) exit(1 << 1); + buf = tmp; + } + memcpy(&buf[bytes - sizeof(byte)], byte, sizeof(byte)); + } + + if (!feof(f)) exit(2 << 1); + fclose(f); + + buf[bytes] = 0; + + return buf; +} + +#endif + +#if defined(__FreeBSD__) || defined(__DragonFly__) +static long int +_sysctlfromname(const char *name, void *mib, int depth, size_t *len) +{ + long int result; + + if (sysctlnametomib(name, mib, len) < 0) + return -1; + + *len = sizeof(result); + if (sysctl(mib, depth, &result, len, NULL, 0) < 0) + return -1; + + return result; +} + +#endif + +static int +cpu_count(void) +{ + int cores = 0; +#if defined(__linux__) + char buf[4096]; + FILE *f; + int line = 0; + + f = fopen("/proc/stat", "r"); + if (!f) return 0; + + while (fgets(buf, sizeof(buf), f)) + { + if (line) + { + if (!strncmp(buf, "cpu", 3)) + cores++; + else + break; + } + line++; + } + + fclose(f); +#elif defined(__MacOS__) || defined(__FreeBSD__) || defined(__DragonFly__) || defined(__OpenBSD__) || defined(__NetBSD__) + size_t len; + int mib[2] = { CTL_HW, HW_NCPU }; + + len = sizeof(cores); + if (sysctl(mib, 2, &cores, &len, NULL, 0) < 0) + return 0; +#endif + return cores; +} + +static void +_cpu_state_get(cpu_core_t **cores, int ncpu) +{ + int diff_total, diff_idle; + double ratio, percent; + unsigned long total, idle, used; + cpu_core_t *core; +#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__OpenBSD__) || defined(__NetBSD__) + size_t size; + int i, j; +#endif +#if defined(__FreeBSD__) || defined(__DragonFly__) + if (!ncpu) + return; + size = sizeof(unsigned long) * (CPU_STATES * ncpu); + unsigned long cpu_times[ncpu][CPU_STATES]; + + if (sysctlbyname("kern.cp_times", cpu_times, &size, NULL, 0) < 0) + return; + + for (i = 0; i < ncpu; i++) { + core = cores[i]; + unsigned long *cpu = cpu_times[i]; + + total = 0; + for (j = 0; j < CPU_STATES; j++) + total += cpu[j]; + + idle = cpu[4]; + + diff_total = total - core->total; + diff_idle = idle - core->idle; + if (diff_total == 0) diff_total = 1; + + ratio = diff_total / 100.0; + used = diff_total - diff_idle; + percent = used / ratio; + + if (percent > 100) percent = 100; + else if (percent < 0) + percent = 0; + + core->percent = percent; + core->total = total; + core->idle = idle; + } +#elif defined(__OpenBSD__) + unsigned long cpu_times[CPU_STATES]; + if (!ncpu) + return; + if (ncpu == 1) + { + core = cores[0]; + int cpu_time_mib[] = { CTL_KERN, KERN_CPTIME }; + size = CPU_STATES * sizeof(unsigned long); + if (sysctl(cpu_time_mib, 2, &cpu_times, &size, NULL, 0) < 0) + return; + + total = 0; + for (j = 0; j < CPU_STATES; j++) + total += cpu_times[j]; + + idle = cpu_times[4]; + + diff_total = total - core->total; + diff_idle = idle - core->idle; + if (diff_total == 0) diff_total = 1; + + ratio = diff_total / 100.0; + used = diff_total - diff_idle; + percent = used / ratio; + + if (percent > 100) percent = 100; + else if (percent < 0) + percent = 0; + + core->percent = percent; + core->total = total; + core->idle = idle; + } + else if (ncpu > 1) + { + for (i = 0; i < ncpu; i++) { + core = cores[i]; + int cpu_time_mib[] = { CTL_KERN, KERN_CPTIME2, 0 }; + size = CPU_STATES * sizeof(unsigned long); + cpu_time_mib[2] = i; + if (sysctl(cpu_time_mib, 3, &cpu_times, &size, NULL, 0) < 0) + return; + + total = 0; + for (j = 0; j < CPU_STATES; j++) + total += cpu_times[j]; + + idle = cpu_times[4]; + + diff_total = total - core->total; + if (diff_total == 0) diff_total = 1; + + diff_idle = idle - core->idle; + ratio = diff_total / 100.0; + used = diff_total - diff_idle; + percent = used / ratio; + + if (percent > 100) percent = 100; + else if (percent < 0) + percent = 0; + + core->percent = percent; + core->total = total; + core->idle = idle; + } + } +#elif defined(__linux__) + char *buf, name[128]; + int i; + + buf = Fcontents("/proc/stat"); + if (!buf) return; + + for (i = 0; i < ncpu; i++) { + core = cores[i]; + snprintf(name, sizeof(name), "cpu%d", i); + char *line = strstr(buf, name); + if (line) + { + line = strchr(line, ' ') + 1; + unsigned long cpu_times[4] = { 0 }; + + if (4 != sscanf(line, "%lu %lu %lu %lu", &cpu_times[0], &cpu_times[1], &cpu_times[2], &cpu_times[3])) + return; + + total = cpu_times[0] + cpu_times[1] + cpu_times[2] + cpu_times[3]; + idle = cpu_times[3]; + diff_total = total - core->total; + if (diff_total == 0) diff_total = 1; + + diff_idle = idle - core->idle; + ratio = diff_total / 100.0; + used = diff_total - diff_idle; + percent = used / ratio; + + if (percent > 100) percent = 100; + else if (percent < 0) + percent = 0; + + core->percent = percent; + core->total = total; + core->idle = idle; + } + } + free(buf); +#elif defined(__MacOS__) + mach_msg_type_number_t count; + processor_cpu_load_info_t load; + mach_port_t mach_port; + unsigned int cpu_count; + int i; + + cpu_count = ncpu; + + count = HOST_CPU_LOAD_INFO_COUNT; + mach_port = mach_host_self(); + if (host_processor_info(mach_port, PROCESSOR_CPU_LOAD_INFO, &cpu_count, (processor_info_array_t *)&load, &count) != KERN_SUCCESS) + exit(4 << 1); + + for (i = 0; i < ncpu; i++) { + core = cores[i]; + + total = load[i].cpu_ticks[CPU_STATE_USER] + load[i].cpu_ticks[CPU_STATE_SYSTEM] + load[i].cpu_ticks[CPU_STATE_IDLE] + load[i].cpu_ticks[CPU_STATE_NICE]; + idle = load[i].cpu_ticks[CPU_STATE_IDLE]; + + diff_total = total - core->total; + if (diff_total == 0) diff_total = 1; + diff_idle = idle - core->idle; + ratio = diff_total / 100.0; + used = diff_total - diff_idle; + percent = used / ratio; + + if (percent > 100) percent = 100; + else if (percent < 0) + percent = 0; + + core->percent = percent; + core->total = total; + core->idle = idle; + } +#endif +} + +static cpu_core_t ** +_cpu_cores_state_get(int *ncpu) +{ + cpu_core_t **cores; + int i; + + *ncpu = cpu_count(); + + cores = malloc((*ncpu) * sizeof(cpu_core_t *)); + + for (i = 0; i < *ncpu; i++) + cores[i] = calloc(1, sizeof(cpu_core_t)); + + _cpu_state_get(cores, *ncpu); + usleep(1000000); + _cpu_state_get(cores, *ncpu); + + return cores; +} + +#if defined(__linux__) +static unsigned long +_meminfo_parse_line(const char *line) +{ + char *p, *tok; + + p = strchr(line, ':') + 1; + while (isspace(*p)) + p++; + tok = strtok(p, " "); + + return atol(tok); +} + +#endif + +static void +_memory_usage_get(meminfo_t *memory) +{ +#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__OpenBSD__) + size_t len = 0; + int i = 0; +#endif + memset(memory, 0, sizeof(meminfo_t)); +#if defined(__linux__) + FILE *f; + unsigned long swap_free = 0, tmp_free = 0, tmp_slab = 0; + char line[256]; + int fields = 0; + + f = fopen("/proc/meminfo", "r"); + if (!f) return; + + while (fgets(line, sizeof(line), f) != NULL) + { + if (!strncmp("MemTotal:", line, 9)) + { + memory->total = _meminfo_parse_line(line); + fields++; + } + else if (!strncmp("MemFree:", line, 8)) + { + tmp_free = _meminfo_parse_line(line); + fields++; + } + else if (!strncmp("Cached:", line, 7)) + { + memory->cached = _meminfo_parse_line(line); + fields++; + } + else if (!strncmp("Slab:", line, 5)) + { + tmp_slab = _meminfo_parse_line(line); + fields++; + } + else if (!strncmp("Buffers:", line, 8)) + { + memory->buffered = _meminfo_parse_line(line); + fields++; + } + else if (!strncmp("Shmem:", line, 6)) + { + memory->shared = _meminfo_parse_line(line); + fields++; + } + else if (!strncmp("SwapTotal:", line, 10)) + { + memory->swap_total = _meminfo_parse_line(line); + fields++; + } + else if (!strncmp("SwapFree:", line, 9)) + { + swap_free = _meminfo_parse_line(line); + fields++; + } + + if (fields >= 8) + break; + } + + memory->cached += tmp_slab; + memory->used = memory->total - tmp_free - memory->cached - memory->buffered; + memory->swap_used = memory->swap_total = swap_free; + + fclose(f); +#elif defined(__FreeBSD__) || defined(__DragonFly__) + int total_pages = 0, free_pages = 0, inactive_pages = 0; + long int result = 0; + int page_size = getpagesize(); + int mib[4] = { CTL_HW, HW_PHYSMEM, 0, 0 }; + + len = sizeof(memory->total); + if (sysctl(mib, 2, &memory->total, &len, NULL, 0) == -1) + return; + memory->total /= 1024; + + total_pages = + _sysctlfromname("vm.stats.vm.v_page_count", mib, 4, &len); + if (total_pages < 0) + return; + + free_pages = _sysctlfromname("vm.stats.vm.v_free_count", mib, 4, &len); + if (free_pages < 0) + return; + + inactive_pages = + _sysctlfromname("vm.stats.vm.v_inactive_count", mib, 4, &len); + if (inactive_pages < 0) + return; + + memory->used = (total_pages - free_pages - inactive_pages) * page_size; + _memsize_bytes_to_kb(&memory->used); + + result = _sysctlfromname("vfs.bufspace", mib, 2, &len); + if (result < 0) + return; + memory->buffered = (result); + _memsize_bytes_to_kb(&memory->buffered); + + result = _sysctlfromname("vm.stats.vm.v_active_count", mib, 4, &len); + if (result < 0) + return; + memory->cached = (result * page_size); + _memsize_bytes_to_kb(&memory->cached); + + result = _sysctlfromname("vm.stats.vm.v_cache_count", mib, 4, &len); + if (result < 0) + return; + memory->shared = (result * page_size); + _memsize_bytes_to_kb(&memory->shared); + + result = _sysctlfromname("vm.swap_total", mib, 2, &len); + if (result < 0) + return; + memory->swap_total = (result / 1024); + + struct xswdev xsw; + /* previous mib is important for this one... */ + + while (i++) + { + mib[2] = i; + len = sizeof(xsw); + if (sysctl(mib, 3, &xsw, &len, NULL, 0) == -1) + break; + + memory->swap_used += xsw.xsw_used * page_size; + } + + memory->swap_used >>= 10; + +#elif defined(__OpenBSD__) + static int mib[] = { CTL_HW, HW_PHYSMEM64 }; + static int bcstats_mib[] = { CTL_VFS, VFS_GENERIC, VFS_BCACHESTAT }; + struct bcachestats bcstats; + static int uvmexp_mib[] = { CTL_VM, VM_UVMEXP }; + struct uvmexp uvmexp; + int nswap, rnswap; + struct swapent *swdev = NULL; + + len = sizeof(memory->total); + if (sysctl(mib, 2, &memory->total, &len, NULL, 0) == -1) + return; + + len = sizeof(uvmexp); + if (sysctl(uvmexp_mib, 2, &uvmexp, &len, NULL, 0) == -1) + return; + + len = sizeof(bcstats); + if (sysctl(bcstats_mib, 3, &bcstats, &len, NULL, 0) == -1) + return; + + /* Don't fail if there's not swap! */ + nswap = swapctl(SWAP_NSWAP, 0, 0); + if (nswap == 0) + goto swap_out; + + swdev = calloc(nswap, sizeof(*swdev)); + if (swdev == NULL) + goto swap_out; + + rnswap = swapctl(SWAP_STATS, swdev, nswap); + if (rnswap == -1) + goto swap_out; + + for (i = 0; i < nswap; i++) { + if (swdev[i].se_flags & SWF_ENABLE) + { + memory->swap_used += (swdev[i].se_inuse / (1024 / DEV_BSIZE)); + memory->swap_total += (swdev[i].se_nblks / (1024 / DEV_BSIZE)); + } + } +swap_out: + if (swdev) + free(swdev); + + memory->total /= 1024; + + memory->cached = (uvmexp.pagesize * bcstats.numbufpages); + _memsize_bytes_to_kb(&memory->cached); + + memory->used = (uvmexp.active * uvmexp.pagesize); + _memsize_bytes_to_kb(&memory->used); + + memory->buffered = (uvmexp.pagesize * (uvmexp.npages - uvmexp.free)); + _memsize_bytes_to_kb(&memory->buffered); + + memory->shared = (uvmexp.pagesize * uvmexp.wired); + _memsize_bytes_to_kb(&memory->shared); +#elif defined(__MacOS__) + int mib[2] = { CTL_HW, HW_MEMSIZE }; + size_t total; + vm_size_t page_size; + mach_port_t mach_port; + mach_msg_type_number_t count; + vm_statistics64_data_t vm_stats; + struct xsw_usage xsu; + + size_t len = sizeof(size_t); + if (sysctl(mib, 2, &total, &len, NULL, 0) == -1) + return; + mach_port = mach_host_self(); + count = sizeof(vm_stats) / sizeof(natural_t); + + total >>= 10; + memory->total = total; + + if (host_page_size(mach_port, &page_size) == KERN_SUCCESS && + host_statistics64(mach_port, HOST_VM_INFO, (host_info64_t)&vm_stats, &count) == KERN_SUCCESS) + { + memory->used = vm_stats.active_count + vm_stats.inactive_count + vm_stats.wire_count * page_size; + memory->used >>= 10; + memory->cached = vm_stats.active_count * page_size; + memory->cached >>= 10; + memory->shared = vm_stats.wire_count * page_size; + memory->shared >>= 10; + memory->buffered = vm_stats.inactive_count * page_size; + memory->buffered >>= 10; + } + + total = sizeof(xsu); + if (sysctlbyname("vm.swapusage", &xsu, &total, NULL, 0) != -1) + { + memory->swap_total = xsu.xsu_total; + memory->swap_used = xsu.xsu_used; + } +#endif +} + +static int +_mixer_master_volume_get(mixer_t *mixer) +{ +#if defined(__OpenBSD__) || defined(__NetBSD__) + int i, fd, devn; + char name[64]; + mixer_devinfo_t dinfo; + mixer_ctrl_t *values = NULL; + mixer_devinfo_t *info = NULL; + + fd = open("/dev/mixer", O_RDONLY); + if (fd < 0) + return 0; + + for (devn = 0;; devn++) { + dinfo.index = devn; + if (ioctl(fd, AUDIO_MIXER_DEVINFO, &dinfo)) + break; + } + + info = calloc(devn, sizeof(*info)); + if (!info) + return 0; + + for (i = 0; i < devn; i++) { + info[i].index = i; + if (ioctl(fd, AUDIO_MIXER_DEVINFO, &info[i]) == -1) + { + --devn; + --i; + mixer->enabled = true; + continue; + } + } + + values = calloc(devn, sizeof(*values)); + if (!values) + return 0; + + for (i = 0; i < devn; i++) { + values[i].dev = i; + values[i].type = info[i].type; + if (info[i].type != AUDIO_MIXER_CLASS) + { + values[i].un.value.num_channels = 2; + if (ioctl(fd, AUDIO_MIXER_READ, &values[i]) == -1) + { + values[i].un.value.num_channels = 1; + if (ioctl(fd, AUDIO_MIXER_READ, &values[i]) + == -1) + return 0; + } + } + } + + for (i = 0; i < devn; i++) { + strlcpy(name, info[i].label.name, sizeof(name)); + if (!strcmp("master", name)) + { + mixer->volume_left = values[i].un.value.level[0]; + mixer->volume_right = values[i].un.value.level[1]; + mixer->enabled = true; + break; + } + } + + close(fd); + + if (values) + free(values); + if (info) + free(info); + +#elif defined(__FreeBSD__) || defined(__DragonFly__) || defined(__linux__) && !defined(HAVE_ALSA) + int bar; + int fd = open("/dev/mixer", O_RDONLY); + if (fd == -1) + return 0; + + if ((ioctl(fd, MIXER_READ(0), &bar)) == -1) + { + return 0; + } + + mixer->enabled = true; + mixer->volume_left = bar & 0x7f; + mixer->volume_right = (bar >> 8) & 0x7f; + close(fd); +#elif defined(__linux__) && defined(HAVE_ALSA) + snd_mixer_t *h; + snd_mixer_elem_t *elem; + snd_mixer_selem_id_t *id; + long int value; + double volume; + + snd_mixer_selem_id_alloca(&id); + snd_mixer_selem_id_set_index(id, 0); + snd_mixer_selem_id_set_name(id, "Master"); + + if ((snd_mixer_open(&h, 0)) == -1) return (0); + if ((snd_mixer_attach(h, "default")) == -1) return(0); + if ((snd_mixer_selem_register(h, NULL, NULL)) == -1) goto out; + if ((snd_mixer_load(h)) == -1) goto out; + + if (!(elem = snd_mixer_find_selem(h, id))) goto out; + + long int max, min; + + snd_mixer_selem_get_playback_volume_range(elem, &min, &max); + snd_mixer_selem_get_playback_volume(elem, 0, &value); + double ratio = max - min / 100; + volume = value / ratio; + + mixer->enabled = true; + mixer->volume_left = mixer->volume_right = volume * 100; +out: + snd_mixer_close(h); +#elif defined(__MacOS__) + AudioDeviceID id; + AudioObjectPropertyAddress prop; + float volume; + unsigned int id_size = sizeof(id); + unsigned int vol_size = sizeof(volume); + + prop.mSelector = kAudioHardwarePropertyDefaultOutputDevice; + prop.mScope = kAudioObjectPropertyScopeGlobal; + prop.mElement = kAudioObjectPropertyElementMaster; + + if (AudioHardwareServiceGetPropertyData(kAudioObjectSystemObject, &prop, 0, NULL, &id_size, &id)) + return (0); + + prop.mSelector = kAudioDevicePropertyVolumeScalar; + prop.mScope = kAudioDevicePropertyScopeOutput; + prop.mElement = 0; + + if (AudioHardwareServiceGetPropertyData(id, &prop, 0, NULL, &vol_size, &volume)) + return (0); + + mixer->volume_left = mixer->volume_right = volume * 100; + + mixer->enabled = true; +#endif + return mixer->enabled; +} + +static void +_temperature_cpu_get(int *temperature) +{ +#if defined(__OpenBSD__) || defined(__NetBSD__) + int mibs[5] = { CTL_HW, HW_SENSORS, 0, 0, 0 }; + int devn, numt; + struct sensor snsr; + size_t slen = sizeof(struct sensor); + struct sensordev snsrdev; + size_t sdlen = sizeof(struct sensordev); + + for (devn = 0;; devn++) { + mibs[2] = devn; + + if (sysctl(mibs, 3, &snsrdev, &sdlen, NULL, 0) == -1) + { + if (errno == ENOENT) + break; + else + continue; + } + if (!strcmp("cpu0", snsrdev.xname)) + break; + else if (!strcmp("km0", snsrdev.xname)) + break; + } + + for (numt = 0; numt < snsrdev.maxnumt[SENSOR_TEMP]; numt++) { + mibs[4] = numt; + + if (sysctl(mibs, 5, &snsr, &slen, NULL, 0) == -1) + continue; + + if (slen > 0 && (snsr.flags & SENSOR_FINVALID) == 0) + break; + } + + if (sysctl(mibs, 5, &snsr, &slen, NULL, 0) + != -1) + { + *temperature = (snsr.value - 273150000) / 1000000.0; + } + else + *temperature = INVALID_TEMP; +#elif defined(__FreeBSD__) || defined(__DragonFly__) + unsigned int value; + size_t len = sizeof(value); + if ((sysctlbyname + ("hw.acpi.thermal.tz0.temperature", &value, &len, NULL, + 0)) != -1) + { + *temperature = (value - 2732) / 10; + } + else + *temperature = INVALID_TEMP; +#elif defined(__linux__) + struct dirent *dh; + DIR *dir; + char path[PATH_MAX]; + + *temperature = INVALID_TEMP; + + dir = opendir("/sys/class/thermal"); + if (!dir) return; + + while ((dh = readdir(dir)) != NULL) + { + if (!strncmp(dh->d_name, "thermal_zone", 12)) + { + snprintf(path, sizeof(path), "/sys/class/thermal/%s/type", dh->d_name); + char *type = Fcontents(path); + if (type) + { + /* This should ensure we get the highest available core temperature */ + if (strstr(type, "_pkg_temp")) + { + snprintf(path, sizeof(path), "/sys/class/thermal/%s/temp", dh->d_name); + char *value = Fcontents(path); + if (value) + { + *temperature = atoi(value) / 1000; + free(value); + free(type); + break; + } + } + free(type); + } + } + } + + closedir(dir); +#elif defined(__MacOS__) + *temperature = INVALID_TEMP; +#endif +} + +static int +_power_battery_count_get(power_t *power) +{ +#if defined(__OpenBSD__) || defined(__NetBSD__) + struct sensordev snsrdev; + size_t sdlen = sizeof(struct sensordev); + int mib[5] = { CTL_HW, HW_SENSORS, 0, 0, 0 }; + int i, devn; + for (devn = 0;; devn++) { + mib[2] = devn; + if (sysctl(mib, 3, &snsrdev, &sdlen, NULL, 0) == -1) + { + if (errno == ENXIO) + continue; + if (errno == ENOENT) + break; + } + + for (i = 0; i < MAX_BATTERIES; i++) { + char buf[64]; + snprintf(buf, sizeof(buf), "acpibat%d", i); + if (!strcmp(buf, snsrdev.xname)) + { + power->bat_mibs[power->battery_count] = + malloc(sizeof(int) * 5); + int *tmp = power->bat_mibs[power->battery_count++]; + tmp[0] = mib[0]; + tmp[1] = mib[1]; + tmp[2] = mib[2]; + } + } + + if (!strcmp("acpiac0", snsrdev.xname)) + { + power->ac_mibs[0] = mib[0]; + power->ac_mibs[1] = mib[1]; + power->ac_mibs[2] = mib[2]; + } + } +#elif defined(__FreeBSD__) || defined(__DragonFly__) + size_t len; + if ((sysctlbyname("hw.acpi.battery.life", NULL, &len, NULL, 0)) != -1) + { + power->bat_mibs[power->battery_count] = malloc(sizeof(int) * 5); + sysctlnametomib("hw.acpi.battery.life", + power->bat_mibs[power->battery_count], &len); + power->battery_count = 1; + } + + if ((sysctlbyname("hw.acpi.acline", NULL, &len, NULL, 0)) != -1) + { + sysctlnametomib("hw.acpi.acline", power->ac_mibs, &len); + } +#elif defined(__linux__) + struct dirent *dh; + DIR *dir; + + dir = opendir("/sys/class/power_supply"); + if (!dir) return 0; + + while ((dh = readdir(dir)) != NULL) + { + if (dh->d_name[0] == '.') continue; + if (!strncmp(dh->d_name, "BAT", 3)) + { + power->battery_names[power->battery_count++] = (char)dh->d_name[3]; + } + } + + closedir(dir); +#endif + return power->battery_count; +} + +static void +_battery_state_get(power_t *power, int *mib) +{ +#if defined(__OpenBSD__) || defined(__NetBSD__) + double charge_full = 0; + double charge_current = 0; + size_t slen = sizeof(struct sensor); + struct sensor snsr; + + mib[3] = 7; + mib[4] = 0; + + if (sysctl(mib, 5, &snsr, &slen, NULL, 0) != -1) + charge_full = (double)snsr.value; + + mib[3] = 7; + mib[4] = 3; + + if (sysctl(mib, 5, &snsr, &slen, NULL, 0) != -1) + charge_current = (double)snsr.value; + + /* ACPI bug workaround... */ + if (charge_current == 0 || charge_full == 0) + { + mib[3] = 8; + mib[4] = 0; + + if (sysctl(mib, 5, &snsr, &slen, NULL, 0) != -1) + charge_full = (double)snsr.value; + + mib[3] = 8; + mib[4] = 3; + + if (sysctl(mib, 5, &snsr, &slen, NULL, 0) != -1) + charge_current = (double)snsr.value; + } + + power->charge_full += charge_full; + power->charge_current += charge_current; +#elif defined(__FreeBSD__) || defined(__DragonFly__) + unsigned int value; + size_t len = sizeof(value); + if ((sysctl(mib, 4, &value, &len, NULL, 0)) != -1) + power->percent = value; +#elif defined(__linux__) + char path[PATH_MAX]; + struct dirent *dh; + DIR *dir; + char *buf, *naming = NULL; + int i = 0; + unsigned long charge_full = 0; + unsigned long charge_current = 0; + + while (power->battery_names[i] != '\0') + { + snprintf(path, sizeof(path), "/sys/class/power_supply/BAT%c", power->battery_names[i]); + dir = opendir(path); + if (!dir) return; + while ((dh = readdir(dir)) != NULL) + { + if (!strcmp(dh->d_name, "energy_full")) + { + naming = "energy"; break; + } + else if (!strcmp(dh->d_name, "capacity_full")) + { + naming = "capacity"; break; + } + } + closedir(dir); + if (!naming) continue; + snprintf(path, sizeof(path), "/sys/class/power_supply/BAT%c/%s_full", power->battery_names[i], naming); + buf = Fcontents(path); + if (buf) + { + charge_full = atol(buf); + free(buf); + } + snprintf(path, sizeof(path), "/sys/class/power_supply/BAT%c/%s_now", power->battery_names[i], naming); + buf = Fcontents(path); + if (buf) + { + charge_current = atol(buf); + free(buf); + } + power->charge_full += charge_full; + power->charge_current += charge_current; + naming = NULL; + i++; + } +#endif +} + +static void +_power_state_get(power_t *power) +{ + int i; +#if defined(__OpenBSD__) || defined(__NetBSD__) + struct sensor snsr; + int have_ac = 0; + size_t slen = sizeof(struct sensor); +#elif defined(__FreeBSD__) || defined(__DragonFly__) + unsigned int value; + size_t len; +#elif defined(__linux__) + char *buf; + int have_ac = 0; +#endif + +#if defined(__OpenBSD__) || defined(__NetBSD__) + power->ac_mibs[3] = 9; + power->ac_mibs[4] = 0; + + if (sysctl(power->ac_mibs, 5, &snsr, &slen, NULL, 0) != -1) + have_ac = (int)snsr.value; +#elif defined(__FreeBSD__) || defined(__DragonFly__) + len = sizeof(value); + if ((sysctl(power->ac_mibs, 3, &value, &len, NULL, 0)) == -1) + { + return; + } + power->have_ac = value; +#elif defined(__linux__) + buf = Fcontents("/sys/class/power_supply/AC/online"); + if (buf) + { + have_ac = atoi(buf); + free(buf); + } +#endif + + for (i = 0; i < power->battery_count; i++) + _battery_state_get(power, power->bat_mibs[i]); + +#if defined(__OpenBSD__) || defined(__NetBSD__) || defined(__linux__) + double percent = + 100 * (power->charge_current / power->charge_full); + + power->percent = percent; + power->have_ac = have_ac; +#elif defined(__FreeBSD__) || defined(__DragonFly__) + len = sizeof(value); + if ((sysctl(power->bat_mibs[0], 4, &value, &len, NULL, 0)) == -1) + { + return; + } + + power->percent = value; + +#endif + for (i = 0; i < power->battery_count; i++) + if (power->bat_mibs[i]) free(power->bat_mibs[i]); +} + +#if defined(__MacOS__) || defined(__FreeBSD__) || defined(__DragonFly__) +static void +_freebsd_generic_network_status(unsigned long int *in, + unsigned long int *out) +{ + struct ifmibdata *ifmd; + size_t len; + int i, count; + len = sizeof(count); + + if (sysctlbyname + ("net.link.generic.system.ifcount", &count, &len, NULL, 0) < 0) + return; + + ifmd = malloc(sizeof(struct ifmibdata)); + if (!ifmd) + return; + + for (i = 1; i <= count; i++) { + int mib[] = { CTL_NET, PF_LINK, NETLINK_GENERIC, IFMIB_IFDATA, i, IFDATA_GENERAL }; + len = sizeof(*ifmd); + if (sysctl(mib, 6, ifmd, &len, NULL, 0) < 0) continue; + if (!strcmp(ifmd->ifmd_name, "lo0")) + continue; + *in += ifmd->ifmd_data.ifi_ibytes; + *out += ifmd->ifmd_data.ifi_obytes; + } + free(ifmd); +} + +#endif + +#if defined(__OpenBSD__) +static void +_openbsd_generic_network_status(unsigned long int *in, + unsigned long int *out) +{ + struct ifaddrs *interfaces, *ifa; + + if (getifaddrs(&interfaces) < 0) + return; + + int sock = socket(AF_INET, SOCK_STREAM, 0); + if (sock < 0) + return; + + for (ifa = interfaces; ifa; ifa = ifa->ifa_next) { + struct ifreq ifreq; + struct if_data if_data; + + ifreq.ifr_data = (void *)&if_data; + strncpy(ifreq.ifr_name, ifa->ifa_name, IFNAMSIZ - 1); + if (ioctl(sock, SIOCGIFDATA, &ifreq) < 0) + return; + + struct if_data *const ifi = &if_data; + if (ifi->ifi_type == IFT_ETHER || + ifi->ifi_type == IFT_FASTETHER || + ifi->ifi_type == IFT_GIGABITETHERNET || + ifi->ifi_type == IFT_IEEE80211) + { + if (ifi->ifi_ibytes) + *in += ifi->ifi_ibytes; + + if (ifi->ifi_obytes) + *out += ifi->ifi_obytes; + } + } + close(sock); +} + +#endif + +#if defined(__linux__) +static void +_linux_generic_network_status(unsigned long int *in, + unsigned long int *out) +{ + FILE *f; + char buf[4096], dummy_s[256]; + unsigned long int tmp_in, tmp_out, dummy; + + f = fopen("/proc/net/dev", "r"); + if (!f) return; + + while (fgets(buf, sizeof(buf), f)) + { + if (17 == sscanf(buf, "%s %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu " + "%lu %lu %lu %lu\n", dummy_s, &tmp_in, &dummy, &dummy, + &dummy, &dummy, &dummy, &dummy, &dummy, &tmp_out, &dummy, + &dummy, &dummy, &dummy, &dummy, &dummy, &dummy)) + { + *in += tmp_in; + *out += tmp_out; + } + } + + fclose(f); +} + +#endif + +static void +_network_transfer_get(results_t *results) +{ + unsigned long first_in = 0, first_out = 0; + unsigned long last_in = 0, last_out = 0; +#if defined(__linux__) + _linux_generic_network_status(&first_in, &first_out); + usleep(1000000); + _linux_generic_network_status(&last_in, &last_out); +#elif defined(__OpenBSD__) + _openbsd_generic_network_status(&first_in, &first_out); + usleep(1000000); + _openbsd_generic_network_status(&last_in, &last_out); +#elif defined(__MacOS__) || defined(__FreeBSD__) || defined(__DragonFly__) + _freebsd_generic_network_status(&first_in, &first_out); + usleep(1000000); + _freebsd_generic_network_status(&last_in, &last_out); +#endif + results->incoming = last_in - first_in; + results->outgoing = last_out - first_out; +} + +static int +percentage(int value, int max) +{ + double avg = (max / 100.0); + double tmp = value / avg; + + return round(tmp); +} + +static void +results_pretty(results_t *results, int *order, int count) +{ + int i, j, flags; + + for (i = 0; i < count; i++) { + flags = order[i]; + if (flags & RESULTS_CPU_CORES) + { + if (results->cpu_count > 1) + printf(" [CPUs]: "); + else + printf(" [CPU]: "); + for (j = 0; j < results->cpu_count; j++) { + printf("%.2f%%", results->cores[j]->percent); + if (j < (results->cpu_count - 1)) + printf(" "); + } + } + else if (flags & RESULTS_CPU) + { + double cpu_percent = 0; + for (j = 0; j < results->cpu_count; j++) + cpu_percent += results->cores[j]->percent; + + printf(" [CPU]: %.2f%%", cpu_percent / results->cpu_count); + } + + if (flags & RESULTS_MEM) + { + unsigned long used = results->memory.used; + unsigned long total = results->memory.total; + char unit; + + if (flags & RESULTS_MEM_GB) + { + unit = 'G'; + _memsize_kb_to_gb(&used); + _memsize_kb_to_gb(&total); + } + else if (flags & RESULTS_MEM_MB) + { + unit = 'M'; + _memsize_kb_to_mb(&used); + _memsize_kb_to_mb(&total); + } + else + unit = 'K'; + + printf(" [MEM]: %lu/%lu%c", used, total, unit); + } + + if (flags & RESULTS_NET) + { + const char *unit = "B/s"; + double incoming = results->incoming; + double outgoing = results->outgoing; + + if ((incoming > 1048576) || (outgoing > 1048576)) + { + incoming = incoming / 1048576; + outgoing = outgoing / 1048576; + unit = "MB/s"; + } + else if (((incoming > 1024) && (incoming < 1048576)) || + ((outgoing > 1024) && (outgoing < 1048576))) + { + incoming /= 1024; + outgoing /= 1024; + unit = "KB/s"; + } + printf(" [NET] %.2f/%.2f %s", incoming, outgoing, unit); + } + + if (flags & RESULTS_TMP) + { + if (results->temperature != INVALID_TEMP) + printf(" [T]: %dC", results->temperature); + } + + if (flags & RESULTS_AUD) + { + if (results->mixer.enabled) + { + uint8_t level = + results->mixer.volume_right > + results->mixer.volume_left ? results->mixer. + volume_right : results->mixer.volume_left; +#if defined(__MacOS__) || defined(__linux__) || defined(__FreeBSD__) || defined(__DragonFly__) + uint8_t perc = percentage(level, 100); + printf(" [VOL]: %d%%", perc); +#elif defined(__OpenBSD__) || defined(__NetBSD__) + uint8_t perc = percentage(level, 255); + printf(" [VOL]: %d%%", perc); +#endif + } + } + + if (flags & RESULTS_PWR) + { + if (results->power.have_ac) + printf(" [AC]: %d%%", results->power.percent); + else if (results->power.battery_count == 0) + printf(" [DC]"); + else + printf(" [DC]: %d%%", results->power.percent); + } + + } + printf("\n"); +} + +static void +results_cpu(cpu_core_t **cores, int cpu_count, int flags) +{ + int i; + + if (flags & RESULTS_CPU_CORES) + { + for (i = 0; i < cpu_count; i++) + printf("%.2f ", cores[i]->percent); + } + else + { + double total = 0; + for (i = 0; i < cpu_count; i++) + total += cores[i]->percent; + printf("%.2f", total / cpu_count); + } + + printf("\n"); +} + +static void +results_mem(meminfo_t *mem, int flags) +{ + unsigned long total, used, cached, buffered; + unsigned long shared, swap_total, swap_used; + + total = mem->total; + used = mem->used; + cached = mem->cached; + buffered = mem->buffered; + shared = mem->shared; + swap_total = mem->swap_total; + swap_used = mem->swap_used; + + if (flags & RESULTS_MEM_MB) + { + _memsize_kb_to_mb(&total); + _memsize_kb_to_mb(&used); + _memsize_kb_to_mb(&cached); + _memsize_kb_to_mb(&buffered); + _memsize_kb_to_mb(&shared); + _memsize_kb_to_mb(&swap_total); + _memsize_kb_to_mb(&swap_used); + } + else if (flags & RESULTS_MEM_GB) + { + _memsize_kb_to_gb(&total); + _memsize_kb_to_gb(&used); + _memsize_kb_to_gb(&cached); + _memsize_kb_to_gb(&buffered); + _memsize_kb_to_gb(&shared); + _memsize_kb_to_gb(&swap_total); + _memsize_kb_to_gb(&swap_used); + } + + printf("%lu %lu %lu %lu %lu %lu %lu\n", + total, used, cached, buffered, shared, swap_total, swap_used); +} + +static void +results_power(power_t *power) +{ + printf("%d %d\n", power->have_ac, power->percent); +} + +static void +results_temperature(int temp) +{ + printf("%d\n", temp); +} + +static void +results_network(results_t *results) +{ + printf("%lu %lu\n", results->incoming, results->outgoing); +} + +static void +results_mixer(mixer_t *mixer) +{ + if (!mixer->enabled) + return; + + printf("%d %d\n", mixer->volume_left, mixer->volume_right); +} + +static void +results_verbose(results_t *results, int *order, int count) +{ + int i, flags; + for (i = 0; i < count; i++) { + flags = order[i]; + if (flags & RESULTS_CPU) + results_cpu(results->cores, results->cpu_count, flags); + else if (flags & RESULTS_MEM) + results_mem(&results->memory, flags); + else if (flags & RESULTS_PWR) + results_power(&results->power); + else if (flags & RESULTS_TMP) + results_temperature(results->temperature); + else if (flags & RESULTS_NET) + results_network(results); + else if (flags & RESULTS_AUD) + results_mixer(&results->mixer); + } +} + +static void *_network_transfer_get_thread_cb(void *arg) +{ + results_t *results = arg; + + _network_transfer_get(results); + + return ((void *) 0); +} + +int +main(int argc, char **argv) +{ + void *ret = NULL; + results_t results; + bool have_battery, status_line = false; + int i, j = 0, flags = 0, error = 0; + int order[argc]; + pthread_t tid; + + memset(&order, 0, sizeof(int) * (argc)); + + for (i = 1; i < argc; i++) { + if ((!strcmp(argv[i], "-h")) || + (!strcmp(argv[i], "-help")) || (!strcmp(argv[i], "--help"))) + { + printf("Usage: tingle [OPTIONS]\n" + " Where OPTIONS can be a combination of\n" + " -c\n" + " Show average CPU usage.\n" + " -C\n" + " Show all CPU cores and usage.\n" + " -k (KB) -m (MB) -g (GB)\n" + " Show memory usage (unit).\n" + " -n\n" + " Show network usage.\n" + " -p\n" + " Show power status (ac and battery percentage).\n" + " -t\n" + " Show temperature sensors (temperature in celcius).\n" + " -a\n" + " Display mixer values (system values).\n" + " -s\n" + " Show all in a nicely formatted status bar format.\n" + " This is the default behaviour with no arguments.\n" + " With other flags specify (in any order) which\n" + " components to display in the status bar.\n" + " -h | -help | --help\n" " This help.\n"); + exit(0); + } + + if (!strcmp(argv[i], "-c")) + order[j] |= RESULTS_CPU; + else if (!strcmp(argv[i], "-C")) + order[j] |= RESULTS_CPU | RESULTS_CPU_CORES; + else if (!strcasecmp(argv[i], "-k")) + order[j] |= RESULTS_MEM; + else if (!strcasecmp(argv[i], "-m")) + order[j] |= RESULTS_MEM | RESULTS_MEM_MB; + else if (!strcasecmp(argv[i], "-g")) + order[j] |= RESULTS_MEM | RESULTS_MEM_GB; + else if (!strcasecmp(argv[i], "-p")) + order[j] |= RESULTS_PWR; + else if (!strcasecmp(argv[i], "-t")) + order[j] |= RESULTS_TMP; + else if (!strcasecmp(argv[i], "-a")) + order[j] |= RESULTS_AUD; + else if (!strcasecmp(argv[i], "-n")) + order[j] |= RESULTS_NET; + else if (!strcasecmp(argv[i], "-s")) + { + status_line = true; + continue; + } + flags |= order[j++]; + } + + if (flags == 0) + { + flags |= RESULTS_DEFAULT; + order[0] |= RESULTS_DEFAULT | RESULTS_MEM_MB; + status_line = true; + } + + memset(&results, 0, sizeof(results_t)); + + if (flags & RESULTS_NET) + { + error = pthread_create(&tid, NULL, _network_transfer_get_thread_cb, &results); + if (error) + _network_transfer_get(&results); + } + + if (flags & RESULTS_CPU) + { + results.cores = _cpu_cores_state_get(&results.cpu_count); + } + + if (flags & RESULTS_MEM) + { + _memory_usage_get(&results.memory); + } + + if (flags & RESULTS_PWR) + { + have_battery = _power_battery_count_get(&results.power); + if (have_battery) + _power_state_get(&results.power); + } + + if (flags & RESULTS_TMP) + { + _temperature_cpu_get(&results.temperature); + } + + if (flags & RESULTS_AUD) + { + _mixer_master_volume_get(&results.mixer); + } + + if (flags & RESULTS_NET && !error) + { + pthread_join(tid, ret); + } + + if (status_line) + { + results_pretty(&results, order, j ? j : 1); + } + else + { + results_verbose(&results, order, j); + } + + if (flags & RESULTS_CPU) + { + for (i = 0; i < results.cpu_count; i++) + { + free(results.cores[i]); + } + free(results.cores); + } + + return EXIT_SUCCESS; +} diff --git a/src/tingle/tmux.conf b/src/tingle/tmux.conf new file mode 100644 index 0000000..01a8a23 --- /dev/null +++ b/src/tingle/tmux.conf @@ -0,0 +1,124 @@ +set-option -g prefix C-j +unbind-key C-b +bind-key b send-prefix +# this is mainly taken from Richo Healey's dotfiles. +# thanks mate. +# TODO +# Finish mapping up all keys +# Learn how splits work +# WISHLIST +# Ideally, update the terminal title/hit the wm for activity on alert + + +# Statusline + +set-window-option -g mode-keys vi # vi key +set-option -g status-keys vi +set -g default-terminal "screen-256color" +# What's the point of vi keys without pane movement? +bind l select-pane -R +bind k select-pane -U +bind j select-pane -D +bind h select-pane -L + +bind < resize-pane -L 2 +bind > resize-pane -R 2 +bind - resize-pane -D 2 +bind + resize-pane -U 2 + +bind ` next-layout +#Ctrl-j-u volume up +bind C-u run-shell -b 'volctl +10' +#Ctrl-j-j volume down +bind C-j run-shell -b 'volctl -10' + +# copy mode to escape key +bind y copy-mode +bind ] paste-buffer +bind-key ^] send-keys Escape ":set paste\ri" \; paste-buffer\; send-keys Escape ":set nopaste\r" +#20:03 < micahcowan> rich0_, just in case, something like ^^ [that] + +# move tmux copy buffer into x clipboard +bind-key C-y save-buffer /tmp/tmux-buffer \; run-shell "cat /tmp/tmux-buffer | xclip" + +# splitting and cycling +unbind % +bind | split-window -h # horizontal split +unbind '"' +bind S split-window -v # vertical split + +set-window-option -g automatic-rename off # auto name + +# messages +set-window-option -g mode-bg black +set-window-option -g mode-fg white +#set-option -g message-bg nta +#set-option -g message-fg black + +# No visual activity +set -g visual-activity off +set -g visual-bell on +set -g status-interval 120 + +# Less obnoxious colors + +set -g pane-active-border-fg black +set -g pane-active-border-bg white +set -g pane-border-fg black +set -g pane-border-bg white + +# Richo's screenlike bindings +bind C-n next +bind C-space next +bind space next +bind C-p prev +bind C-d detach +#unbind C-c +bind C-c new-window +bind K confirm-before kill-pane +bind A command-prompt "rename-window '%%'" + +# bind M set-window-option monitor-activity (toggle) +# bind _ set-window-option monitor-silence 15 (Toggle) + +bind-key C-a last-window +bind-key / command-prompt "split-window -h 'exec man %%'" +bind M set-window-option monitor-activity +bind _ command-prompt "setw monitor-silence '%%'" + +bind I set-window-option synchronize-panes + +# Create an environment that zsh can make sense of +# set-environment -g tmuxTERM $TERM +set-environment -g INSCREEN yes + +# New shells should not inherit pwd +# set -g default-path "." + +# status bar +set-option -g status-bg black +set-option -g status-fg white +set-option -g message-attr none +set-option -g message-bg black +set-option -g message-fg white +set-window-option -g window-status-bell-bg black +set-window-option -g window-status-bell-attr bright +set-window-option -g window-status-activity-bg red +set-window-option -g window-status-activity-attr bright +set-option -g status-interval 5 +set-option -g status-right-length 90 +set-option -g status-right "#[fg=colour255]#(tingle -s -c -m -a -p) #(date +'%H:%M %d-%m-%Y')" +# Cheers Nei on freenode +set-window-option -g window-status-current-format "[#[fg=white]#I:#W#F#[fg=red]]" +set-option -g visual-activity on +set-window-option -g monitor-activity off +set-window-option -g window-status-current-fg red +set-window-option -g window-status-current-attr bright + +# DEBUG +# bind r source ~/.tmux.conf + +# clock +set-window-option -g clock-mode-colour black +set-window-option -g clock-mode-style 24 +# vim: ft=tmux diff --git a/src/tingle/volctl b/src/tingle/volctl new file mode 100755 index 0000000..02a2f8d --- /dev/null +++ b/src/tingle/volctl @@ -0,0 +1,142 @@ +#!/usr/bin/perl + +use strict; +use warnings; + +my $OS = $^O; + +sub Error { + my ($str) = @_; + + print STDERR "Error: $str\n"; + + exit (0 << 1); +} + +sub mixer_levels_get { + my ($left, $right) = @_; + my $pipe_cmd = ""; + + if ($OS eq "openbsd" || $OS eq "netbsd") + { + $pipe_cmd = "mixerctl outputs.master|"; + } + elsif ($OS eq "freebsd" || $OS eq "dragonfly") + { + $pipe_cmd = "mixer vol|"; + } + elsif ($OS eq "darwin") + { + $pipe_cmd = "osascript -e 'get volume settings'|"; + } + elsif ($OS eq "linux") + { + $pipe_cmd = "amixer get Master|"; + } + + open P, "$pipe_cmd" || die "Unable to determine mixer!\n"; + my @lines =

; + close P; + + my $have_mixer = 0; + + foreach my $line (@lines) + { + if ($line =~ m/Left:.*\[(\d+)%\]/) + { + $$left = $1; + $have_mixer = 1; + } + + if ($line =~ m/Right:.*\[(\d+)%\]/) + { + $$right = $1; + $have_mixer = 1; + } + + if ($line =~ m/\Aoutput\svolume:(\d+),/) + { + $$left = $$right = $1; + return 1; + } + elsif ($line =~ m/(\d+),(\d+)/ || $line =~ m/(\d+):(\d+)/) + { + $$left = $1; + $$right = $2; + return 1; + } + } + + return $have_mixer; +} + +sub mixer_levels_set { + my ($new_left, $new_right) = @_; + my $cmd = ""; + + close (STDOUT); + + if ($OS eq "openbsd" || $OS eq "netbsd") + { + $cmd = "mixerctl outputs.master=$new_left,$new_right"; + } + elsif ($OS eq "freebsd" || $OS eq "dragonfly") + { + $cmd = "mixer $new_left:$new_right" + } + elsif ($OS eq "darwin") + { + my $greatest = $new_left > $new_right ? $new_left : $new_right; + my $ratio = 7 / 100; + my $volume = $greatest * $ratio; + $cmd = "osascript -e 'set volume $volume'"; + } + elsif ($OS eq "linux") + { + my $greatest = $new_left > $new_right ? $new_left : $new_right; + $cmd = "amixer set \"Master\" $greatest%"; + } + + return system($cmd); +} + +sub main { + my (@args) = @_; + my $argc = scalar(@args); + my ($inc, $dec) = (0, 0); + my ($left, $right) = (0, 0); + + for (my $i = 0; $i < $argc; $i++) + { + if ($args[$i] =~ /\+(\d+)/) + { + $inc = $1; + } + + if ($args[$i] =~ /\-(\d+)/) + { + $dec = $1 + } + } + + if (!mixer_levels_get(\$left, \$right)) + { + Error("couldn't get current volumes"); + } + + $left -= $dec; $right -= $dec; + $left += $inc; $right += $inc; + + if ($OS eq "linux" || $OS eq "darwin" || $OS eq "freebsd" || $OS eq "dragonfly") + { + if ($left < 0) { $left = 0; }; + if ($left > 100) { $left = 100; }; + if ($right < 0) { $right = 0; }; + if ($right > 100) { $right = 100 }; + } + + return mixer_levels_set($left, $right); +} + +exit(main(@ARGV)); +