Adding upstream version 0.9.0.
[debian/lxpanel.git] / plugins / batt / batt_sys.c
CommitLineData
6b775dbb
AG
1/*
2 * batt_sys.h
3 *
4 * Copyright 2009 Juergen Hötzel <juergen@archlinux.org>
0688b017 5 * 2015 Henry Gebhardt <hsggebhardt@googlemail.com>
7a1c5048 6 * 2015 Stanislav Kozina, Ersin <xersin@users.sf.net>
6b775dbb
AG
7 *
8 * Parts shameless stolen and glibified from acpi package
9 * Copyright (C) 2001 Grahame Bowland <grahame@angrygoats.net>
10 * (C) 2008-2009 Michael Meskes <meskes@debian.org>
11 *
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
25 * MA 02110-1301, USA.
26 */
27
28
29#ifdef HAVE_CONFIG_H
30# include <config.h>
31#endif
32
33#include "batt_sys.h"
34#include <glib/gstdio.h>
35
36/* shrug: get rid of this */
37#include <stdlib.h>
38#include <string.h>
39
40battery* battery_new() {
41 static int battery_num = 1;
42 battery * b = g_new0 ( battery, 1 );
43 b->type_battery = TRUE;
7a1c5048 44 //b->capacity_unit = "mAh";
6b775dbb
AG
45 b->energy_full = -1;
46 b->charge_full = -1;
47 b->voltage_now = -1;
48 b->energy_full_design = -1;
49 b->charge_full_design = -1;
50 b->energy_now = -1;
51 b->charge_now = -1;
52 b->current_now = -1;
53 b->power_now = -1;
54 b->state = NULL;
55 b->battery_num = battery_num;
56 b->seconds = -1;
57 b->percentage = -1;
7a1c5048 58 //b->poststr = NULL;
6b775dbb
AG
59 battery_num++;
60 return b;
61}
62
63
64static gchar* parse_info_file(battery *b, char *sys_file)
65{
66 char *buf = NULL;
7a1c5048 67 GString *filename = g_string_new(ACPI_PATH_SYS_POWER_SUPPLY);
6b775dbb
AG
68
69 g_string_append_printf (filename, "/%s/%s", b->path, sys_file);
70
71 if (g_file_get_contents(filename->str, &buf, NULL, NULL) == TRUE) {
7a1c5048 72 g_strstrip( buf );
6b775dbb
AG
73 }
74
75 g_string_free(filename, TRUE);
76
7a1c5048 77 return buf;
6b775dbb
AG
78}
79
80/* get_gint_from_infofile():
81 * If the sys_file exists, then its value is converted to an int,
82 * divided by 1000, and returned.
83 * Failure is indicated by returning -1. */
84static gint get_gint_from_infofile(battery *b, gchar *sys_file)
85{
86 gchar *file_content = parse_info_file(b, sys_file);
7a1c5048 87 gint value = -1;
6b775dbb
AG
88
89 if (file_content != NULL)
7a1c5048
AG
90 value = atoi(file_content) / 1000;
91 g_free(file_content);
6b775dbb 92
7a1c5048 93 return value;
6b775dbb
AG
94}
95
96static gchar* get_gchar_from_infofile(battery *b, gchar *sys_file)
97{
98 return parse_info_file(b, sys_file);
99}
100
7a1c5048 101#if 0 /* never used */
6b775dbb
AG
102void battery_print(battery *b, int show_capacity)
103{
104 if ( b->type_battery )
105 {
106 if (b->state) {
107
108 printf("%s %d: %s, %d%%", BATTERY_DESC, b->battery_num - 1, b->state, b->percentage);
109
110 if (b->seconds > 0) {
111 int hours = b->seconds / 3600;
112 int seconds = b->seconds - 3600 * hours;
113 int minutes = seconds / 60;
114 seconds -= 60 * minutes;
115 printf(", %02d:%02d:%02d%s", hours, minutes, seconds,
116 b->poststr);
117 } else if (b->poststr != NULL) {
118 printf(", %s", b->poststr);
119 }
120
121 printf("\n");
122
123 if (show_capacity && b->charge_full_design > 0) {
124 int percentage = -1;
125 int charge_full = -1;
126 if (b->charge_full <= 100) {
127 /* some broken systems just give a percentage here */
128 percentage = b->charge_full;
129 charge_full = percentage * b->charge_full_design / 100;
130 } else {
131 percentage = b->charge_full * 100 / b->charge_full_design;
132 charge_full = b->charge_full;
133 }
134 if (percentage > 100)
135 percentage = 100;
136
137 printf ("%s %d: design capacity %d %s, "
138 "last full capacity %d %s = %d%%\n",
139 BATTERY_DESC, b->battery_num - 1, b->charge_full_design,
140 b->capacity_unit, charge_full, b->capacity_unit,
141 percentage);
142 }
143 }
144 }
145}
7a1c5048 146#endif
6b775dbb
AG
147
148static gboolean battery_inserted(gchar* path)
149{
150 if (path == NULL)
151 return FALSE;
152
7a1c5048 153 GString *dirname = g_string_new(ACPI_PATH_SYS_POWER_SUPPLY);
6b775dbb
AG
154 GDir *dir;
155
156 g_string_append_printf (dirname, "/%s/", path);
157 dir = g_dir_open(dirname->str, 0, NULL);
158 if (dir)
159 g_dir_close(dir);
160 g_string_free(dirname, TRUE);
161
162 return dir ? TRUE : FALSE;
163}
164
165
166battery* battery_update(battery *b)
167{
168 gchar *gctmp;
169
170 if (b == NULL)
171 return NULL;
172
173 if (!battery_inserted(b->path))
174 return NULL;
175
176 /* read from sysfs */
177 b->charge_now = get_gint_from_infofile(b, "charge_now");
178 b->energy_now = get_gint_from_infofile(b, "energy_now");
179
180 b->current_now = get_gint_from_infofile(b, "current_now");
181 b->power_now = get_gint_from_infofile(b, "power_now");
182 /* FIXME: Some battery drivers report -1000 when the discharge rate is
183 * unavailable. Others use negative values when discharging. Best we can do
184 * is to treat -1 as an error, and take the absolute value otherwise.
185 * Ideally the kernel would not export the sysfs file when the value is not
186 * available. */
187 if (b->current_now < -1)
188 b->current_now = - b->current_now;
189
190 b->charge_full = get_gint_from_infofile(b, "charge_full");
191 b->energy_full = get_gint_from_infofile(b, "energy_full");
192
193 b->charge_full_design = get_gint_from_infofile(b, "charge_full_design");
194 b->energy_full_design = get_gint_from_infofile(b, "energy_full_design");
195
196 b->voltage_now = get_gint_from_infofile(b, "voltage_now");
197
198 gctmp = get_gchar_from_infofile(b, "type");
199 b->type_battery = gctmp ? (strcasecmp(gctmp, "battery") == 0) : TRUE;
7a1c5048 200 g_free(gctmp);
6b775dbb 201
7a1c5048 202 g_free(b->state);
6b775dbb
AG
203 b->state = get_gchar_from_infofile(b, "status");
204 if (!b->state)
205 b->state = get_gchar_from_infofile(b, "state");
206 if (!b->state) {
207 if (b->charge_now != -1 || b->energy_now != -1
208 || b->charge_full != -1 || b->energy_full != -1)
7a1c5048 209 b->state = g_strdup("available");
6b775dbb 210 else
7a1c5048 211 b->state = g_strdup("unavailable");
6b775dbb
AG
212 }
213
7a1c5048 214#if 0 /* those conversions might be good for text prints but are pretty wrong for tooltip and calculations */
6b775dbb
AG
215 /* convert energy values (in mWh) to charge values (in mAh) if needed and possible */
216
217 if (b->energy_full != -1 && b->charge_full == -1) {
218 if (b->voltage_now != -1 && b->voltage_now != 0) {
219 b->charge_full = b->energy_full * 1000 / b->voltage_now;
220 } else {
221 b->charge_full = b->energy_full;
222 b->capacity_unit = "mWh";
223 }
224 }
225
226 if (b->energy_full_design != -1 && b->charge_full_design == -1) {
227 if (b->voltage_now != -1 && b->voltage_now != 0) {
228 b->charge_full_design = b->energy_full_design * 1000 / b->voltage_now;
229 } else {
230 b->charge_full_design = b->energy_full_design;
231 b->capacity_unit = "mWh";
232 }
233 }
234
235 if (b->energy_now != -1 && b->charge_now == -1) {
236 if (b->voltage_now != -1 && b->voltage_now != 0) {
237 b->charge_now = b->energy_now * 1000 / b->voltage_now;
238 if (b->current_now != -1)
239 b->current_now = b->current_now * 1000 / b->voltage_now;
240 } else {
241 b->charge_now = b->energy_now;
242 }
243 }
244
245 if (b->power_now < -1)
246 b->power_now = - b->power_now;
247 else if (b->power_now == -1 && b->voltage_now != -1 && b->current_now != -1)
248 b->power_now = b->voltage_now * b->current_now / 1000; // P = U*I
249 if (b->power_now != -1 && b->current_now == -1) {
250 if (b->voltage_now != -1 && b->voltage_now != 0)
251 b->current_now = b->power_now * 1000 / b->voltage_now;
252 }
7a1c5048 253#endif
6b775dbb
AG
254
255 if (b->charge_full < MIN_CAPACITY)
256 b->percentage = 0;
257 else {
258 int promille = (b->charge_now * 1000) / b->charge_full;
259 b->percentage = (promille + 5) / 10; /* round properly */
260 }
261 if (b->percentage > 100)
262 b->percentage = 100;
263
264
265 if (b->current_now == -1) {
7a1c5048 266 //b->poststr = "rate information unavailable";
6b775dbb
AG
267 b->seconds = -1;
268 } else if (!strcasecmp(b->state, "charging")) {
269 if (b->current_now > MIN_PRESENT_RATE) {
270 b->seconds = 3600 * (b->charge_full - b->charge_now) / b->current_now;
7a1c5048 271 //b->poststr = " until charged";
6b775dbb 272 } else {
7a1c5048 273 //b->poststr = "charging at zero rate - will never fully charge.";
6b775dbb
AG
274 b->seconds = -1;
275 }
276 } else if (!strcasecmp(b->state, "discharging")) {
277 if (b->current_now > MIN_PRESENT_RATE) {
278 b->seconds = 3600 * b->charge_now / b->current_now;
7a1c5048 279 //b->poststr = " remaining";
6b775dbb 280 } else {
7a1c5048 281 //b->poststr = "discharging at zero rate - will never fully discharge.";
6b775dbb
AG
282 b->seconds = -1;
283 }
284 } else {
7a1c5048 285 //b->poststr = NULL;
6b775dbb
AG
286 b->seconds = -1;
287 }
288
289 return b;
290}
291
292
7a1c5048 293battery *battery_get(int battery_number) {
6b775dbb
AG
294 GError * error = NULL;
295 const gchar *entry;
7a1c5048
AG
296 gchar *batt_name = NULL;
297 gchar *batt_path = NULL;
298 GDir * dir = g_dir_open( ACPI_PATH_SYS_POWER_SUPPLY, 0, &error );
6b775dbb 299 battery *b = NULL;
7a1c5048
AG
300 int i;
301
6b775dbb
AG
302 if ( dir == NULL )
303 {
304 g_warning( "NO ACPI/sysfs support in kernel: %s", error->message );
305 return NULL;
306 }
7a1c5048
AG
307
308 /* Try the expected path in sysfs first */
309 batt_name = g_strdup_printf(ACPI_BATTERY_DEVICE_NAME "%d", battery_number);
310 batt_path = g_strdup_printf(ACPI_PATH_SYS_POWER_SUPPLY "/%s", batt_name);
311 if (g_file_test(batt_path, G_FILE_TEST_IS_DIR) == TRUE) {
312 b = battery_new();
313 b->path = g_strdup( batt_name);
314 battery_update ( b );
315
316 if (!b->type_battery) {
317 g_warning( "Not a battery: %s", batt_path );
318 battery_free(b);
319 b = NULL;
320 }
321 }
322
323 g_free(batt_name);
324 g_free(batt_path);
325
326 if (b != NULL)
327 goto done;
328
329 /*
330 * We didn't find the expected path in sysfs.
331 * Walk the dir and blindly return n-th entry.
332 */
333 i = 0;
6b775dbb
AG
334 while ( ( entry = g_dir_read_name (dir) ) != NULL )
335 {
336 b = battery_new();
337 b->path = g_strdup( entry );
338 battery_update ( b );
7a1c5048
AG
339
340 /* We're looking for a battery with the selected ID */
341 if (b->type_battery == TRUE) {
342 if (i == battery_number)
343 break;
344 i++;
6b775dbb 345 }
7a1c5048
AG
346 battery_free(b);
347 b = NULL;
6b775dbb 348 }
7a1c5048
AG
349 if (b != NULL)
350 g_warning( "Battery entry " ACPI_BATTERY_DEVICE_NAME "%d not found, using %s",
351 battery_number, b->path);
352 else
353 g_warning( "Battery %d not found", battery_number );
354done:
6b775dbb
AG
355 g_dir_close( dir );
356 return b;
357}
358
359void battery_free(battery* bat)
360{
361 if (bat) {
362 g_free(bat->path);
7a1c5048 363 g_free(bat->state);
6b775dbb
AG
364 g_free(bat);
365 }
366}
367
368gboolean battery_is_charging( battery *b )
369{
370 if (!b->state)
371 return TRUE; // Same as "Unkown"
8713e384
AG
372 return ( strcasecmp( b->state, "Unknown" ) == 0
373 || strcasecmp( b->state, "Full" ) == 0
374 || strcasecmp( b->state, "Charging" ) == 0
375 || b->current_now == 0 ); /* bug sf.net, #720 */
6b775dbb
AG
376}
377
378gint battery_get_remaining( battery *b )
379{
380 return b->seconds;
381}
382
383
384/* vim: set sw=4 et sts=4 : */