Skip to content
Snippets Groups Projects
Select Git revision
  • 2d78daa75e90357cf9389daccbd06d75d4e96e42
  • jk default protected
2 results

osd-mpc-volume

Blame
  • osd-batt.c 5.95 KiB
    /*
     *	A Simple Battery Status Display via OSD
     *
     *	(c) 2007--2012 Martin Mares <mj@ucw.cz>
     */
    
    #undef DEBUG
    
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
    #include <dirent.h>
    #include <getopt.h>
    #include <fcntl.h>
    
    #include "util.h"
    #include "osd.h"
    
    static int check_mode;
    static int check_every;
    static int warn_threshold = 600;
    
    static int total_now, total_full, discharge_rate;
    static int charge_time, discharge_time;
    static int ac_online;
    static unsigned int present_mask, charge_mask, discharge_mask;
    
    static unsigned int last_charge_mask, last_discharge_mask;
    static int last_ac_online = -1;
    
    #define MAX_BATTS 4
    #define BATT_NAME_LEN 32
    static char batt_names[MAX_BATTS][BATT_NAME_LEN];
    
    static char sys_dir[256];
    #define BUFSIZE 256
    
    static int sys_read(char *buf, char *attribute)
    {
      char name[256];
      snprintf(name, sizeof(name), "%s/%s", sys_dir, attribute);
    
      int fd = open(name, O_RDONLY);
      if (fd < 0)
        return 0;
    
      int n = read(fd, buf, BUFSIZE);
      close(fd);
      if (n < 0)
        return 0;
    
      buf[BUFSIZE-1] = 0;
      char *nl = strchr(buf, '\n');
      if (nl)
        *nl = 0;
      DBG("\t%s=%s\n", attribute, buf);
      return 1;
    }
    
    static int sys_read_int(char *attribute, int default_value)
    {
      char buf[BUFSIZE];
      if (!sys_read(buf, attribute) || !buf[0])
        return default_value;
      else
        return atoi(buf);
    }
    
    static void parse_ac(void)
    {
      ac_online = sys_read_int("online", 0);
    }
    
    static int get_batt_id(char *batt_name)
    {
      for (int i=0; i<MAX_BATTS; i++)
        {
          if (!strcmp(batt_names[i], batt_name))
    	return i;
          if (!batt_names[i][0])
    	{
    	  snprintf(batt_names[i], BATT_NAME_LEN, "%s", batt_name);
    	  return i;
    	}
        }
      return MAX_BATTS;
    }
    
    static void parse_batt(char *batt_name)
    {
      int batt_id = get_batt_id(batt_name);
      DBG("\t-> id %d\n", batt_id);
    
      if (!sys_read_int("present", 1))
        return;
    
      char status[BUFSIZE];
      int charging = sys_read(status, "status") && !strcmp(status, "Charging");
      int charge_full = sys_read_int("charge_full", 0);
      int charge_now = sys_read_int("charge_now", 0);
      int current_now = sys_read_int("current_now", 0);
    
      present_mask |= 1 << batt_id;
      total_now += charge_now;
      total_full += charge_full;
      if (charging && current_now > 0)
        {
          charge_mask |= 1 << batt_id;
          int ch = (long long)(charge_full - charge_now)*3600 / current_now;
          if (ch > charge_time)
    	charge_time = ch;
        }
      else if (current_now > 0)
        {
          discharge_mask |= 1 << batt_id;
          discharge_rate += current_now;
        }
    }
    
    static void scan(void)
    {
      ac_online = 0;
      charge_time = discharge_time = 0;
      total_now = total_full = 0;
      discharge_rate = 0;
      present_mask = charge_mask = discharge_mask = 0;
    
      const char dir[] = "/sys/class/power_supply";
      DIR *d = opendir(dir);
      if (!d)
        return;
    
      struct dirent *e;
      while (e = readdir(d))
        {
          if (e->d_name[0] == '.')
    	continue;
          snprintf(sys_dir, sizeof(sys_dir), "%s/%s", dir, e->d_name);
          DBG("%s\n", sys_dir);
    
          char type[BUFSIZE];
          if (!sys_read(type, "type"))
    	continue;
    
          if (!strcmp(type, "Mains"))
    	parse_ac();
          else if (!strcmp(type, "Battery"))
    	parse_batt(e->d_name);
        }
      if (discharge_rate)
        discharge_time = (long long) total_now*3600 / discharge_rate;
      else
        discharge_time = 1000000;
    
      closedir(d);
      DBG("=> Capacity: now=%d full=%d\n", total_now, total_full);
      DBG("=> Charge: mask=%d time=%d\n", charge_mask, charge_time);
      DBG("=> Discharge: mask=%d rate=%d time=%d\n", discharge_mask, discharge_rate, discharge_time);
    }
    
    static char *batt_mask(char *p, unsigned int mask)
    {
      if (present_mask & (present_mask-1))
        {
          char *p0 = p;
          for (int i=0; mask; i++)
            if (mask & (1 << i))
    	  {
    	    *p = (p == p0) ? ' ' : '+';
    	    p++;
    	    p += sprintf(p, "B%d", i);
    	    mask &= ~(1 << i);
    	  }
        }
      return p;
    }
    
    static void show(void)
    {
      char status[256];
      char *p = status;
      if (total_full)
        p += sprintf(p, "%d%%", (int)((long long)100*total_now/total_full));
      else
        p += sprintf(p, "??%%");
      if (discharge_mask && discharge_time < 1000000)
        {
          p += sprintf(p, "  %d:%02d  remains", discharge_time/3600, (discharge_time/60)%60);
          batt_mask(p, discharge_mask);
        }
      else if (charge_mask)
        {
          p += sprintf(p, "  %d:%02d  charging", charge_time/3600, (charge_time/60)%60);
          batt_mask(p, charge_mask);
        }
      else if (ac_online)
        p += sprintf(p, " AC");
      else
        p += sprintf(p, " BATT");
    
      struct osd_msg *msg = osd_new_msg();
      osd_add_line(msg, NULL, status);
      osd_send(msg);
    }
    
    static void show_if_warn(void)
    {
      if (discharge_mask && discharge_time < warn_threshold ||
          last_ac_online >= 0 && (
    	charge_mask != last_charge_mask ||
    	discharge_mask != last_discharge_mask ||
    	ac_online != last_ac_online))
        show();
    
      last_charge_mask = charge_mask;
      last_discharge_mask = discharge_mask;
      last_ac_online = ac_online;
    }
    
    static void NONRET
    usage(void)
    {
      fprintf(stderr, "\
    Usage: osd-batt <options>\n\
    \n\
    Options:\n\
    -c, --check\t\tDisplay status only if battery is low\n\
    -e, --check-every=<sec>\tRun on background and check every <sec> seconds\n\
    -w, --warn=<sec>\tBattery is low if less than <sec> seconds remain (default: 600)\n\
    ");
      exit(1);
    }
    
    static const char short_opts[] = "ce:w:";
    
    static const struct option long_opts[] = {
      { "check",		no_argument,		NULL,	'c' },
      { "check-every",	required_argument,	NULL,	'e' },
      { "warn",		required_argument,	NULL,	'w' },
      { NULL,		0,			NULL,	0   },
    };
    
    int main(int argc, char **argv)
    {
      int opt;
      while ((opt = getopt_long(argc, argv, short_opts, long_opts, NULL)) >= 0)
        switch (opt)
          {
          case 'c':
    	check_mode++;
    	break;
          case 'e':
    	check_every = atoi(optarg);
    	break;
          case 'w':
    	warn_threshold = atoi(optarg);
    	break;
          default:
    	usage();
          }
      if (optind < argc)
        usage();
    
      if (check_every)
        {
          osd_fork();
          for (;;)
    	{
    	  scan();
    	  show_if_warn();
    	  osd_wait(check_every);
    	}
        }
    
      scan();
      if (check_mode)
        show_if_warn();
      else
        show();
    
      return 0;
    }