wmclockmon: update change-log
[dockapps.git] / wmbattery / upower.c
blob6a006337999f9a069b81ac2e6e9c80ae144675f7
1 /* Not particularly good interface to hal, for programs that used to use
2 * apm.
3 */
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <string.h>
8 #include <unistd.h>
9 #include <errno.h>
10 #include <sys/types.h>
11 #include <sys/socket.h>
12 #include <sys/wait.h>
13 #include <upower.h>
14 #include "apm.h"
16 #define MAX_RETRIES 3
18 struct context {
19 int current;
20 int needed;
21 guint state;
22 int percentage;
23 gboolean ac;
24 int time;
27 static void get_devinfo(gpointer device, gpointer result)
29 gboolean online;
30 gdouble percentage;
31 guint state;
32 guint kind;
33 gint64 time_to_empty;
34 gint64 time_to_full;
35 struct context *ctx = result;
37 g_object_get(G_OBJECT(device), "percentage", &percentage,
38 "online", &online,
39 "state", &state,
40 "kind", &kind,
41 "time-to-empty", &time_to_empty,
42 "time-to-full", &time_to_full,
43 NULL);
44 if (kind == UP_DEVICE_KIND_BATTERY) {
45 if (ctx->current == ctx->needed) {
46 ctx->percentage = (int)percentage;
47 ctx->state = state;
48 if (time_to_empty)
49 ctx->time = time_to_empty;
50 else
51 ctx->time = time_to_full;
53 ctx->current++;
54 } else if (kind == UP_DEVICE_KIND_LINE_POWER) {
55 ctx->ac |= online;
59 /* Fill the passed apm_info struct. */
60 static int upower_read_child(int battery, apm_info *info)
62 UpClient * up;
63 GPtrArray *devices = NULL;
65 up = up_client_new();
67 if (!up)
68 return -1;
70 #if !UP_CHECK_VERSION(0, 9, 99)
71 /* Allow a battery that was not present before to appear. */
72 up_client_enumerate_devices_sync(up, NULL, NULL);
73 #endif
75 devices = up_client_get_devices(up);
77 if (!devices)
78 return -1;
80 info->battery_flags = 0;
81 info->using_minutes = 0;
83 struct context ctx = {
84 .current = 0,
85 .needed = battery - 1,
86 .state = UP_DEVICE_STATE_UNKNOWN,
87 .percentage = -1,
88 .ac = FALSE,
89 .time = -1
92 g_ptr_array_foreach(devices, &get_devinfo, &ctx);
94 info->ac_line_status = ctx.ac;
96 /* remaining_time and charge_level.percentage are not a mandatory
97 * keys, so if not present, -1 will be returned */
98 info->battery_time = ctx.time;
99 info->battery_percentage = ctx.percentage;
100 if (ctx.state == UP_DEVICE_STATE_DISCHARGING) {
101 info->battery_status = BATTERY_STATUS_CHARGING;
102 /* charge_level.warning and charge_level.low are not
103 * required to be available; this is good enough */
104 if (info->battery_percentage < 1)
105 info->battery_status = BATTERY_STATUS_CRITICAL;
106 else if (info->battery_percentage < 10)
107 info->battery_status = BATTERY_STATUS_LOW;
108 } else if (info->ac_line_status && ctx.state == UP_DEVICE_STATE_CHARGING) {
109 info->battery_status = BATTERY_STATUS_CHARGING;
110 info->battery_flags = info->battery_flags | BATTERY_FLAGS_CHARGING;
111 } else if (info->ac_line_status) {
112 /* Must be fully charged. */
113 info->battery_status = BATTERY_STATUS_HIGH;
114 } else {
115 fprintf(stderr, "unknown battery state\n");
118 if (ctx.percentage < 0) {
119 info->battery_percentage = 0;
120 info->battery_time = 0;
121 info->battery_status = BATTERY_STATUS_ABSENT;
124 g_ptr_array_unref(devices);
125 return 0;
128 static int upower_read_work(int battery, apm_info *info)
130 int sp[2];
131 int child;
132 int status;
133 ssize_t cv;
135 if (socketpair(AF_UNIX, SOCK_DGRAM, 0, sp) < 0) {
136 fprintf(stderr, "socketpair: %s", strerror(errno));
137 goto fail;
140 child = fork();
141 if (child < 0) {
142 fprintf(stderr, "fork: %s", strerror(errno));
143 goto fail_close;
146 if (child == 0) {
147 /* child process does work, writes failure or result back to parent */
148 close(sp[0]);
149 status = upower_read_child(battery, info);
150 if (status < 0) {
151 cv = send(sp[1], &status, sizeof(status), 0);
153 else {
154 cv = send(sp[1], info, sizeof(*info), 0);
156 exit(cv < 0);
158 close(sp[1]);
160 child = waitpid(child, &status, 0);
161 if (child < 0) {
162 fprintf(stderr, "waitpid: %s status=%d", strerror(errno), status);
163 goto fail_close0;
166 cv = recv(sp[0], info, sizeof(*info), 0);
167 if (cv < 0) {
168 fprintf(stderr, "recv: %s", strerror(errno));
169 goto fail_close0;
171 else if (cv == sizeof(status)) {
172 memcpy(&status, info, sizeof(status));
173 goto fail_close0;
175 else if (cv != sizeof(*info)) {
176 fprintf(stderr, "recv: unexpected size %zd", cv);
177 goto fail_close0;
180 close(sp[0]);
181 return 0;
183 fail_close:
184 close(sp[1]);
185 fail_close0:
186 close(sp[0]);
187 fail:
188 return -1;
191 int upower_supported(void)
193 apm_info info;
194 return !(upower_read_work(1, &info) < 0);
198 int upower_read(int battery, apm_info *info)
200 static int retries = 0;
202 if (upower_read_work(battery, info) < 0) {
203 retries++;
204 if (retries < MAX_RETRIES)
205 return 0; /* fine immediately after hibernation */
206 else
207 return -1;
210 retries = 0;
211 return 0;