]>
Commit | Line | Data |
---|---|---|
ef0963b8 SW |
1 | // For vasprintf |
2 | #define _GNU_SOURCE | |
3 | ||
5a945372 | 4 | #include "chart.h" |
0f9e308a SW |
5 | #include <ctype.h> |
6 | #include <errno.h> | |
eda1f8c1 | 7 | #include <glob.h> |
79ac753b | 8 | #include <gtk/gtk.h> |
0f9e308a | 9 | #include <math.h> |
ef0963b8 | 10 | #include <stdarg.h> |
0f9e308a SW |
11 | #include <stdio.h> |
12 | #include <stdlib.h> | |
eda1f8c1 | 13 | #include <string.h> |
0f9e308a | 14 | |
ef0963b8 SW |
15 | char *sasprintf(const char *fmt, ...) { |
16 | char *result; | |
17 | va_list args; | |
18 | va_start(args, fmt); | |
19 | if (vasprintf(&result, fmt, args) == -1) { | |
20 | fprintf(stderr, "Can't allocate memory!?\n"); | |
21 | abort(); | |
22 | } | |
23 | va_end(args); | |
24 | return result; | |
25 | } | |
26 | ||
fc1a2773 | 27 | struct State { |
7281755a SW |
28 | char *voltage_filename; |
29 | char *current_filename; | |
fc1a2773 SW |
30 | BVChart *voltage; |
31 | BVChart *current; | |
32 | }; | |
33 | ||
0f9e308a SW |
34 | static float fatof(const char *filename) { |
35 | FILE *f = fopen(filename, "r"); | |
36 | if (f == NULL) { | |
37 | return NAN; | |
38 | } | |
39 | ||
40 | const int bufsize = 1024; | |
41 | char buf[bufsize]; | |
42 | const int read = fread(buf, sizeof(char), bufsize, f); | |
43 | if (read == 0 || read == bufsize || ferror(f)) { | |
44 | fclose(f); | |
45 | return NAN; | |
46 | } | |
47 | fclose(f); | |
48 | ||
49 | char *end; | |
50 | errno = 0; | |
51 | float val = strtof(buf, &end); | |
52 | int parsed = end - buf; | |
53 | gboolean parsed_all = parsed == read; | |
54 | gboolean parsed_all_but_space = parsed == read - 1 && isspace(*end); | |
55 | gboolean parse_ok = parsed_all || parsed_all_but_space; | |
56 | if (errno != 0 || parsed == 0 || !parse_ok) { | |
57 | return NAN; | |
58 | } | |
59 | return val; | |
60 | } | |
61 | ||
7281755a | 62 | static gboolean collect_data(struct State *state) { |
51a39b46 SW |
63 | float now = g_get_monotonic_time() / 1e6; |
64 | fprintf(stderr, "%f: Voltage: %f, current: %f\n", now, | |
65 | fatof(state->voltage_filename), fatof(state->current_filename)); | |
6f50819e SW |
66 | return TRUE; |
67 | } | |
68 | ||
fc1a2773 | 69 | static void activate(GtkApplication *app, gpointer user_data) { |
79ac753b | 70 | |
cecfa926 | 71 | GtkWidget *window = gtk_application_window_new(app); |
a8d35c4c | 72 | gtk_window_set_title(GTK_WINDOW(window), "BatteryViewer"); |
79ac753b SW |
73 | gtk_window_set_default_size(GTK_WINDOW(window), 200, 200); |
74 | ||
1bd5b327 SW |
75 | GtkWidget *box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 1); |
76 | gtk_container_add(GTK_CONTAINER(window), box); | |
77 | ||
fc1a2773 SW |
78 | struct State *state = (struct State *)user_data; |
79 | state->voltage = BV_CHART(bv_chart_new()); | |
80 | bv_chart_add_point(state->voltage, 0.0, 0.0); | |
81 | bv_chart_add_point(state->voltage, 1.0, 1.0); | |
82 | bv_chart_add_point(state->voltage, 3.0, 2.0); | |
1bd5b327 SW |
83 | gboolean expand = TRUE; |
84 | gboolean fill = TRUE; | |
85 | guint padding = 0; | |
fc1a2773 SW |
86 | gtk_box_pack_start(GTK_BOX(box), GTK_WIDGET(state->voltage), expand, fill, |
87 | padding); | |
1bd5b327 | 88 | |
fc1a2773 SW |
89 | state->current = BV_CHART(bv_chart_new()); |
90 | bv_chart_add_point(state->current, 0.0, 1.0); | |
91 | bv_chart_add_point(state->current, 1.0, 0.0); | |
92 | gtk_box_pack_end(GTK_BOX(box), GTK_WIDGET(state->current), expand, fill, | |
93 | padding); | |
79ac753b | 94 | |
a9a80e4e | 95 | gtk_widget_show_all(window); |
6f50819e SW |
96 | |
97 | g_timeout_add_seconds(1, (GSourceFunc)collect_data, user_data); | |
79ac753b SW |
98 | } |
99 | ||
eda1f8c1 SW |
100 | char *find_battery_dir() { |
101 | glob_t globbuf = {.gl_offs = 0}; | |
102 | char pattern[] = "/sys/class/power_supply/*[Bb][Aa][Tt]*"; | |
103 | if (glob(pattern, GLOB_ERR, NULL, &globbuf) != 0 || globbuf.gl_pathc == 0) { | |
104 | fprintf(stderr, "Could not find battery dir %s\n", pattern); | |
105 | exit(1); | |
106 | } | |
107 | if (globbuf.gl_pathc != 1) { | |
108 | // TODO: Filter for directories that have {voltage,current}_now files. | |
109 | // TODO: Support multiple batteries | |
110 | fprintf(stderr, "Multiple batteries found. Arbitrarily choosing %s\n", | |
111 | globbuf.gl_pathv[0]); | |
112 | } | |
113 | char *dir = strdup(globbuf.gl_pathv[0]); | |
114 | globfree(&globbuf); | |
115 | return dir; | |
116 | } | |
117 | ||
79ac753b | 118 | int main(int argc, char **argv) { |
eda1f8c1 | 119 | char *battery_dir = find_battery_dir(); |
7281755a | 120 | struct State state = { |
ef0963b8 SW |
121 | .voltage_filename = sasprintf("%s/voltage_now", battery_dir), |
122 | .current_filename = sasprintf("%s/current_now", battery_dir), | |
7281755a | 123 | }; |
79ac753b | 124 | |
a8d35c4c SW |
125 | GtkApplication *app = gtk_application_new("com.scottworley.batteryviewer", |
126 | G_APPLICATION_DEFAULT_FLAGS); | |
fc1a2773 | 127 | g_signal_connect(app, "activate", G_CALLBACK(activate), &state); |
cecfa926 | 128 | int status = g_application_run(G_APPLICATION(app), argc, argv); |
79ac753b SW |
129 | g_object_unref(app); |
130 | ||
131 | return status; | |
132 | } |