i3 - improved tiling WM


Implement battery functionality for NetBSD users

Patch status: merged

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/522/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,266 @@ 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
+                                continue;
153
+                        }
154
+
155
+                        if (obj3 &&
156
+                            strlen(prop_string_cstring_nocopy(obj3)) == 14 &&
157
+                            strncmp("discharge rate",
158
+                                    prop_string_cstring_nocopy(obj3),
159
+                                    14) == 0)
160
+                        {
161
+                                obj3 = prop_dictionary_get(obj2, "cur-value");
162
+                                present_rate = prop_number_integer_value(obj3);
163
+                                continue;
164
+                        }
165
+
166
+                        if (obj3 &&
167
+                            strlen(prop_string_cstring_nocopy(obj3)) == 13 &&
168
+                            strncmp("last full cap",
169
+                                    prop_string_cstring_nocopy(obj3),
170
+                                    13) == 0)
171
+                        {
172
+                                obj3 = prop_dictionary_get(obj2, "cur-value");
173
+                                last_full_cap = prop_number_integer_value(obj3);
174
+                                continue;
175
+                        }
176
+
177
+                        if (obj3 &&
178
+                            strlen(prop_string_cstring_nocopy(obj3)) == 7 &&
179
+                            strncmp("voltage",
180
+                                    prop_string_cstring_nocopy(obj3),
181
+                                    7) == 0)
182
+                        {
183
+                                obj3 = prop_dictionary_get(obj2, "cur-value");
184
+                                voltage = prop_number_integer_value(obj3);
185
+                                continue;
186
+                        }
187
+                }
188
+                prop_object_iterator_release(iter2);
189
+        }
190
+
191
+        prop_object_iterator_release(iter);
192
+        prop_object_release(dict);
193
+        close(fd);
194
+
195
+        if (! is_found) {
196
+                OUTPUT_FULL_TEXT(format_down);
197
+                return;
198
+        }
199
+
200
+        if (last_full_capacity)
201
+                full_design = last_full_cap;
202
+
203
+        if (! watt_as_unit) {
204
+                present_rate = (((float)voltage / 1000.0) * ((float)present_rate / 1000.0));
205
+                remaining = (((float)voltage / 1000.0) * ((float)remaining / 1000.0));
206
+                full_design = (((float)voltage / 1000.0) * ((float)full_design / 1000.0));
207
+        }
208
+
209
+        float percentage_remaining =
210
+                (((float)remaining / (float)full_design) * 100);
211
+
212
+        if (integer_battery_capacity)
213
+                (void)snprintf(percentagebuf,
214
+                           sizeof(percentagebuf),
215
+                           "%d%%",
216
+                           (int) percentage_remaining);
217
+        else
218
+                (void)snprintf(percentagebuf,
219
+                           sizeof(percentagebuf),
220
+                           "%.02f%%",
221
+                           percentage_remaining);
222
+
223
+        /*
224
+         * Handle percentage low_threshold here, and time low_threshold when
225
+         * we have it.
226
+         */
227
+        if (status == CS_DISCHARGING && low_threshold > 0) {
228
+                if (strcasecmp(threshold_type, "percentage") == 0
229
+                    && (((float)remaining / (float)full_design) * 100) < low_threshold) {
230
+                        START_COLOR("color_bad");
231
+                        colorful_output = true;
232
+                }
233
+        }
234
+
235
+        if (is_full)
236
+                (void)snprintf(statusbuf, sizeof(statusbuf), "%s", BATT_STATUS_NAME(CS_FULL));
237
+        else
238
+                (void)snprintf(statusbuf, sizeof(statusbuf), "%s", BATT_STATUS_NAME(status));
239
+
240
+        /*
241
+         * The envsys(4) ACPI routines do not appear to provide a 'time
242
+         * remaining' figure, so we must deduce it.
243
+         */
244
+        float remaining_time;
245
+        int seconds, hours, minutes, seconds_remaining;
246
+
247
+        if (status == CS_CHARGING)
248
+                remaining_time = ((float)full_design - (float)remaining)
249
+                        / (float)present_rate;
250
+        else if (status == CS_DISCHARGING)
251
+                remaining_time = ((float)remaining / (float)present_rate);
252
+        else remaining_time = 0;
253
+
254
+        seconds_remaining = (int)(remaining_time * 3600.0);
255
+
256
+        hours = seconds_remaining / 3600;
257
+        seconds = seconds_remaining - (hours * 3600);
258
+        minutes = seconds / 60;
259
+        seconds -= (minutes * 60);
260
+
261
+        if (status != CS_CHARGING) {
262
+                if (hide_seconds)
263
+                        (void)snprintf(remainingbuf, sizeof(remainingbuf), "%02d:%02d",
264
+                                max(hours, 0), max(minutes, 0));
265
+                else
266
+                        (void)snprintf(remainingbuf, sizeof(remainingbuf), "%02d:%02d:%02d",
267
+                                max(hours, 0), max(minutes, 0), max(seconds, 0));
268
+
269
+                if (low_threshold > 0) {
270
+                        if (strcasecmp(threshold_type, "time") == 0
271
+                            && ((float) seconds_remaining / 60.0) < (u_int) low_threshold) {
272
+                                START_COLOR("color_bad");
273
+                                colorful_output = true;
274
+                        }
275
+                }
276
+        } else {
277
+                if (hide_seconds)
278
+                        (void)snprintf(remainingbuf, sizeof(remainingbuf), "(%02d:%02d until full)",
279
+                                max(hours, 0), max(minutes, 0));
280
+                else
281
+                        (void)snprintf(remainingbuf, sizeof(remainingbuf), "(%02d:%02d:%02d until full)",
282
+                                max(hours, 0), max(minutes, 0), max(seconds, 0));
283
+        }
284
+
285
+        empty_time = time(NULL);
286
+        empty_time += seconds_remaining;
287
+        empty_tm = localtime(&empty_time);
288
+
289
+        /* No need to show empty time if battery is charging */
290
+        if (status != CS_CHARGING) {
291
+                if (hide_seconds)
292
+                        (void)snprintf(emptytimebuf, sizeof(emptytimebuf), "%02d:%02d",
293
+                                max(empty_tm->tm_hour, 0), max(empty_tm->tm_min, 0));
294
+                else
295
+                        (void)snprintf(emptytimebuf, sizeof(emptytimebuf), "%02d:%02d:%02d",
296
+                                max(empty_tm->tm_hour, 0), max(empty_tm->tm_min, 0), max(empty_tm->tm_sec, 0));
297
+        }
298
+
299
+        (void)snprintf(consumptionbuf, sizeof(consumptionbuf), "%1.2fW",
300
+                ((float)present_rate / 1000.0 / 1000.0));
301
 #endif
302
 
303
 #define EAT_SPACE_FROM_OUTPUT_IF_EMPTY(_buf) \