i3 - improved tiling WM


Implement battery functionality for NetBSD users

Patch status: superseded

Patch by Alexander Vasarab

Long description:

This patch takes a similar approach as the NetBSD CPU temperature
code in that it uses proplib(3) to walk dictionaries supplied by
envsys(4).

In addition to providing the basic functionality, it:

* Provides all existing format specifiers (%emptytime %consumption
  %status %percentage %remaining)
* Respects all existing config options (hide_seconds, low_threshold,
  integer_battery_capacity, last_full_capacity)
* Projects "time until full" when battery status is CS_CHARGING

To apply this patch, use:
curl http://cr.i3wm.org/patch/519/raw.patch | git am

b/src/print_battery_info.c

24
@@ -21,6 +21,12 @@
25
 #include <machine/apmvar.h>
26
 #endif
27
 
28
+#if defined(__NetBSD__)
29
+#include <fcntl.h>
30
+#include <prop/proplib.h>
31
+#include <sys/envsys.h>
32
+#endif
33
+
34
 #define BATT_STATUS_NAME(status) \
35
     (status == CS_CHARGING ? "CHR" : \
36
         (status == CS_DISCHARGING ? "BAT" : "FULL"))
37
@@ -316,6 +322,268 @@ void print_battery_info(yajl_gen json_gen, char *buffer, int number, const char
38
 
39
 	if (colorful_output)
40
 		END_COLOR;
41
+#elif defined(__NetBSD__)
42
+        /*
43
+         * Using envsys(4) via sysmon(4).
44
+         */
45
+        int fd, rval, last_full_cap;
46
+        bool is_found = false;
47
+        char *sensor_desc;
48
+        bool is_full = false;
49
+
50
+        prop_dictionary_t dict;
51
+        prop_array_t array;
52
+        prop_object_iterator_t iter;
53
+        prop_object_iterator_t iter2;
54
+        prop_object_t obj, obj2, obj3, obj4, obj5;
55
+
56
+        asprintf(&sensor_desc, "acpibat%d", number);
57
+
58
+        fd = open("/dev/sysmon", O_RDONLY);
59
+        if (fd < 0) {
60
+                OUTPUT_FULL_TEXT("can't open /dev/sysmon");
61
+                return;
62
+        }
63
+
64
+        rval = prop_dictionary_recv_ioctl(fd, ENVSYS_GETDICTIONARY, &dict);
65
+        if (rval == -1) {
66
+                close(fd);
67
+                return;
68
+        }
69
+
70
+        if (prop_dictionary_count(dict) == 0) {
71
+                prop_object_release(dict);
72
+                close(fd);
73
+                return;
74
+        }
75
+
76
+        iter = prop_dictionary_iterator(dict);
77
+        if (iter == NULL) {
78
+                prop_object_release(dict);
79
+                close(fd);
80
+        }
81
+
82
+        /* iterate over the dictionary returned by the kernel */
83
+        while ((obj = prop_object_iterator_next(iter)) != NULL) {
84
+                /* skip this dict if it's not what we're looking for */
85
+                if ((strlen(prop_dictionary_keysym_cstring_nocopy(obj)) == strlen(sensor_desc)) &&
86
+                    (strncmp(sensor_desc,
87
+                            prop_dictionary_keysym_cstring_nocopy(obj),
88
+                            strlen(sensor_desc)) != 0))
89
+                        continue;
90
+
91
+                is_found = true;
92
+
93
+                array = prop_dictionary_get_keysym(dict, obj);
94
+                if (prop_object_type(array) != PROP_TYPE_ARRAY) {
95
+                        prop_object_iterator_release(iter);
96
+                        prop_object_release(dict);
97
+                        close(fd);
98
+                        return;
99
+                }
100
+
101
+                iter2 = prop_array_iterator(array);
102
+                if (!iter2) {
103
+                        prop_object_iterator_release(iter);
104
+                        prop_object_release(dict);
105
+                        close(fd);
106
+                        return;
107
+                }
108
+
109
+                /* iterate over array of dicts specific to target battery */
110
+                while ((obj2 = prop_object_iterator_next(iter2)) != NULL) {
111
+                        obj3 = prop_dictionary_get(obj2, "description");
112
+
113
+                        if (obj3 &&
114
+                            strlen(prop_string_cstring_nocopy(obj3)) == 8 &&
115
+                            strncmp("charging",
116
+                                    prop_string_cstring_nocopy(obj3),
117
+                                    8) == 0)
118
+                        {
119
+                                obj3 = prop_dictionary_get(obj2, "cur-value");
120
+
121
+                                if (prop_number_integer_value(obj3))
122
+                                        status = CS_CHARGING;
123
+                                else
124
+                                        status = CS_DISCHARGING;
125
+
126
+                                continue;
127
+                        }
128
+
129
+                        if (obj3 &&
130
+                            strlen(prop_string_cstring_nocopy(obj3)) == 6 &&
131
+                            strncmp("charge",
132
+                                    prop_string_cstring_nocopy(obj3),
133
+                                    6) == 0)
134
+                        {
135
+                                obj3 = prop_dictionary_get(obj2, "cur-value");
136
+                                obj4 = prop_dictionary_get(obj2, "max-value");
137
+                                obj5 = prop_dictionary_get(obj2, "type");
138
+
139
+                                remaining = prop_number_integer_value(obj3);
140
+                                full_design = prop_number_integer_value(obj4);
141
+
142
+                                if (remaining == full_design)
143
+                                        is_full = true;
144
+
145
+                                if (strncmp("Ampere hour",
146
+                                            prop_string_cstring_nocopy(obj5),
147
+                                            11) == 0)
148
+                                        watt_as_unit = false;
149
+                                else
150
+                                        watt_as_unit = true;
151
+
152
+                                fprintf(stdout, "watt_as_unit = %d", watt_as_unit);
153
+
154
+                                continue;
155
+                        }
156
+
157
+                        if (obj3 &&
158
+                            strlen(prop_string_cstring_nocopy(obj3)) == 14 &&
159
+                            strncmp("discharge rate",
160
+                                    prop_string_cstring_nocopy(obj3),
161
+                                    14) == 0)
162
+                        {
163
+                                obj3 = prop_dictionary_get(obj2, "cur-value");
164
+                                present_rate = prop_number_integer_value(obj3);
165
+                                continue;
166
+                        }
167
+
168
+                        if (obj3 &&
169
+                            strlen(prop_string_cstring_nocopy(obj3)) == 13 &&
170
+                            strncmp("last full cap",
171
+                                    prop_string_cstring_nocopy(obj3),
172
+                                    13) == 0)
173
+                        {
174
+                                obj3 = prop_dictionary_get(obj2, "cur-value");
175
+                                last_full_cap = prop_number_integer_value(obj3);
176
+                                continue;
177
+                        }
178
+
179
+                        if (obj3 &&
180
+                            strlen(prop_string_cstring_nocopy(obj3)) == 7 &&
181
+                            strncmp("voltage",
182
+                                    prop_string_cstring_nocopy(obj3),
183
+                                    7) == 0)
184
+                        {
185
+                                obj3 = prop_dictionary_get(obj2, "cur-value");
186
+                                voltage = prop_number_integer_value(obj3);
187
+                                continue;
188
+                        }
189
+                }
190
+                prop_object_iterator_release(iter2);
191
+        }
192
+
193
+        prop_object_iterator_release(iter);
194
+        prop_object_release(dict);
195
+        close(fd);
196
+
197
+        if (! is_found) {
198
+                OUTPUT_FULL_TEXT(format_down);
199
+                return;
200
+        }
201
+
202
+        if (last_full_capacity)
203
+                full_design = last_full_cap;
204
+
205
+        if (! watt_as_unit) {
206
+                present_rate = (((float)voltage / 1000.0) * ((float)present_rate / 1000.0));
207
+                remaining = (((float)voltage / 1000.0) * ((float)remaining / 1000.0));
208
+                full_design = (((float)voltage / 1000.0) * ((float)full_design / 1000.0));
209
+        }
210
+
211
+        float percentage_remaining =
212
+                (((float)remaining / (float)full_design) * 100);
213
+
214
+        if (integer_battery_capacity)
215
+                (void)snprintf(percentagebuf,
216
+                           sizeof(percentagebuf),
217
+                           "%d%%",
218
+                           (int) percentage_remaining);
219
+        else
220
+                (void)snprintf(percentagebuf,
221
+                           sizeof(percentagebuf),
222
+                           "%.02f%%",
223
+                           percentage_remaining);
224
+
225
+        /*
226
+         * Handle percentage low_threshold here, and time low_threshold when
227
+         * we have it.
228
+         */
229
+        if (status == CS_DISCHARGING && low_threshold > 0) {
230
+                if (strcasecmp(threshold_type, "percentage") == 0
231
+                    && (((float)remaining / (float)full_design) * 100) < low_threshold) {
232
+                        START_COLOR("color_bad");
233
+                        colorful_output = true;
234
+                }
235
+        }
236
+
237
+        if (is_full)
238
+                (void)snprintf(statusbuf, sizeof(statusbuf), "%s", BATT_STATUS_NAME(CS_FULL));
239
+        else
240
+                (void)snprintf(statusbuf, sizeof(statusbuf), "%s", BATT_STATUS_NAME(status));
241
+
242
+        /*
243
+         * The envsys(4) ACPI routines do not appear to provide a 'time
244
+         * remaining' figure, so we must deduce it.
245
+         */
246
+        float remaining_time;
247
+        int seconds, hours, minutes, seconds_remaining;
248
+
249
+        if (status == CS_CHARGING)
250
+                remaining_time = ((float)full_design - (float)remaining)
251
+                        / (float)present_rate;
252
+        else if (status == CS_DISCHARGING)
253
+                remaining_time = ((float)remaining / (float)present_rate);
254
+        else remaining_time = 0;
255
+
256
+        seconds_remaining = (int)(remaining_time * 3600.0);
257
+
258
+        hours = seconds_remaining / 3600;
259
+        seconds = seconds_remaining - (hours * 3600);
260
+        minutes = seconds / 60;
261
+        seconds -= (minutes * 60);
262
+
263
+        if (status != CS_CHARGING) {
264
+                if (hide_seconds)
265
+                        (void)snprintf(remainingbuf, sizeof(remainingbuf), "%02d:%02d",
266
+                                max(hours, 0), max(minutes, 0));
267
+                else
268
+                        (void)snprintf(remainingbuf, sizeof(remainingbuf), "%02d:%02d:%02d",
269
+                                max(hours, 0), max(minutes, 0), max(seconds, 0));
270
+
271
+                if (low_threshold > 0) {
272
+                        if (strcasecmp(threshold_type, "time") == 0
273
+                            && ((float) seconds_remaining / 60.0) < (u_int) low_threshold) {
274
+                                START_COLOR("color_bad");
275
+                                colorful_output = true;
276
+                        }
277
+                }
278
+        } else {
279
+                if (hide_seconds)
280
+                        (void)snprintf(remainingbuf, sizeof(remainingbuf), "(%02d:%02d until full)",
281
+                                max(hours, 0), max(minutes, 0));
282
+                else
283
+                        (void)snprintf(remainingbuf, sizeof(remainingbuf), "(%02d:%02d:%02d until full)",
284
+                                max(hours, 0), max(minutes, 0), max(seconds, 0));
285
+        }
286
+
287
+        empty_time = time(NULL);
288
+        empty_time += seconds_remaining;
289
+        empty_tm = localtime(&empty_time);
290
+
291
+        /* No need to show empty time if battery is charging */
292
+        if (status != CS_CHARGING) {
293
+                if (hide_seconds)
294
+                        (void)snprintf(emptytimebuf, sizeof(emptytimebuf), "%02d:%02d",
295
+                                max(empty_tm->tm_hour, 0), max(empty_tm->tm_min, 0));
296
+                else
297
+                        (void)snprintf(emptytimebuf, sizeof(emptytimebuf), "%02d:%02d:%02d",
298
+                                max(empty_tm->tm_hour, 0), max(empty_tm->tm_min, 0), max(empty_tm->tm_sec, 0));
299
+        }
300
+
301
+        (void)snprintf(consumptionbuf, sizeof(consumptionbuf), "%1.2fW",
302
+                ((float)present_rate / 1000.0 / 1000.0));
303
 #endif
304
 
305
 #define EAT_SPACE_FROM_OUTPUT_IF_EMPTY(_buf) \