battery_info.c (3174B)
1 /* See LICENSE for license details. */ 2 struct bat_arg { 3 s8 bat; /* BAT name (ex. s8("BAT0")) */ 4 char *pre; /* prefix for percentages less than thres */ 5 char *suf; /* suffix for percentages less than thres */ 6 i32 thres; /* % threshold to consider low (-1 to disable) */ 7 }; 8 9 struct linux_battery_data { Stream path_base; i64 energy_full; }; 10 11 static BLOCK_UPDATE_FN(battery_info_update) 12 { 13 struct bat_arg *ba = b->arg; 14 struct linux_battery_data *lbd = b->user_data; 15 16 char *pre = ba->pre ? ba->pre : ""; 17 char *suf = ba->suf ? ba->suf : ""; 18 19 i32 h, m; 20 i64 power_now, energy_now; 21 f64 timeleft; 22 23 size sidx = lbd->path_base.write_index; 24 25 stream_push_s8(&lbd->path_base, s8("/energy_now")); 26 energy_now = read_i64(stream_ensure_c_str(&lbd->path_base)); 27 lbd->path_base.write_index = sidx; 28 29 f32 percent = 100 * (energy_now / (f64)lbd->energy_full) + 0.5; 30 b32 warn = percent < ba->thres; 31 32 char state_buffer[16] = {0}; 33 stream_push_s8(&lbd->path_base, s8("/status")); 34 s8 state = s8_trim_space(read_s8(stream_ensure_c_str(&lbd->path_base), 35 (s8){.len = sizeof(state_buffer), 36 .data = (u8 *)state_buffer})); 37 if (state.len <= 0) state = s8("Unknown"); 38 lbd->path_base.write_index = sidx; 39 state.data[state.len] = 0; 40 41 i64 len; 42 /* NOTE(rnp): proper devices use negative power to indicate discharging but that 43 * is not always the case. The status string can mostly be trusted */ 44 if (s8_equal(state, s8("Discharging"))) { 45 stream_push_s8(&lbd->path_base, s8("/power_now")); 46 power_now = read_i64(stream_ensure_c_str(&lbd->path_base)); 47 if (!power_now) power_now = 1; 48 lbd->path_base.write_index = sidx; 49 50 timeleft = energy_now / (f64)ABS(power_now); 51 h = timeleft; 52 m = (timeleft - (f64)h) * 60; 53 54 len = snprintf(buffer, sizeof(buffer), "%s%d%% (%d:%02d)%s", warn? pre : "", 55 (i32)percent, h, m, warn? suf : ""); 56 } else { 57 len = snprintf(buffer, sizeof(buffer), "%s%d%% (%s)%s", warn? pre : "", 58 (i32)percent, (char *)state.data, warn? suf : ""); 59 } 60 buffer[len] = 0; 61 b->len = snprintf(b->data, sizeof(b->data), b->fmt, buffer); 62 } 63 64 #define LINUX_BAT_INFO_STRS \ 65 X("/energy_full") \ 66 X("/energy_now") \ 67 X("/power_now") \ 68 X("/status") 69 70 static BLOCK_INIT_FN(battery_info_init) 71 { 72 struct bat_arg *ba = b->arg; 73 struct linux_battery_data *lbd; 74 b->user_data = lbd = push_struct(a, struct linux_battery_data); 75 76 size max_length = 0; 77 #define X(cstr) if ((sizeof(cstr) - 1) > max_length) max_length = sizeof(cstr) - 1; 78 LINUX_BAT_INFO_STRS 79 #undef X 80 81 size needed_length = max_length + ba->bat.len + sizeof("/sys/class/power_supply/"); 82 lbd->path_base = stream_alloc(a, needed_length); 83 84 stream_push_s8(&lbd->path_base, s8("/sys/class/power_supply/")); 85 stream_push_s8(&lbd->path_base, ba->bat); 86 size sidx = lbd->path_base.write_index; 87 stream_push_s8(&lbd->path_base, s8("/energy_full")); 88 lbd->energy_full = read_i64(stream_ensure_c_str(&lbd->path_base)); 89 if (!lbd->energy_full) 90 die("battery_info_init: failed to read battery capacity\n"); 91 lbd->path_base.write_index = sidx; 92 93 battery_info_update(b); 94 }